Debug Param Update Issues
This guide helps you debug issues where device parameter updates are not reflecting in the GET API response, or where parameter changes made through the app are not reaching the device. Parameters (params) are the real-time values of device attributes such as power state, brightness, temperature, etc.
Before you start, have these ready:
- Node ID of the affected device
- The parameter name that is not updating (e.g.,
brightness,output) - Approximate time when the issue was observed
Step 1: Identify Your Symptom
| Symptom | Go to |
|---|---|
| Device updates params but GET API returns old/stale values | Device Params Not Reflecting in GET API |
| GET API returns error 102006 "Device reported state not found" | No Param Data Found |
| App sends param update but device does not receive it | App Param Update Not Reaching Device |
| API returns error 102001 "Node is offline" when setting params | Node Showing Offline — Cannot Set Params |
| Params update sometimes but are intermittently missing | Intermittent Param Updates |
| Getting a specific error code (102xxx) | Error Code Reference |
Param Update Flow Overview
Understanding the flow helps identify at which stage the failure occurred.
Device → Cloud (reported params):
Step 1 Device publishes params to MQTT topic: node/<node_id>/params/local
Step 2 AWS IoT Rule "esp_set_params" routes the message to SQS queue: esp-SetDeviceShadowSQS
Step 3 Lambda esp-SetDeviceShadow reads from SQS, merges new values into existing params
Step 4 Lambda writes merged params to DynamoDB nodes_v3 table (reported_params row)
Step 5 App calls GET /user/nodes/params → Lambda esp-NodeState reads from nodes_v3 → returns params
App → Device (setting params):
Step 1 App calls PUT /user/nodes/params with new param values
Step 2 Lambda esp-NodeState checks node connectivity (online/offline) in DynamoDB
Step 3 If online → publishes to MQTT topic: node/<node_id>/params/remote
Step 4 Device receives the message and updates its state
Step 5 Device reports updated state back via node/<node_id>/params/local (Flow A above)
Device Params Not Reflecting in GET API
The most common scenario — the device is publishing param updates but the GET API still returns old values.
Step 1: Check if SetDeviceShadow Lambda is processing the updates
Go to CloudWatch → Logs Insights, select /aws/lambda/esp-SetDeviceShadow, and run:
fields @timestamp, @message
| sort @timestamp desc
| filter @message like "<node_id>"
Set the time range to cover when the param update was expected.
What to look for:
| Log message | Meaning | Next Step |
|---|---|---|
| Recent entries with no errors | Lambda processed the update — check DynamoDB (Step 2) | Go to Step 2 |
"Json is invalid" | Device sent a malformed JSON payload — silently dropped | Fix the device firmware's JSON serialization |
"Failed to update node shadow" | DynamoDB write failed | Check DynamoDB table health; check Lambda IAM permissions |
"ConditionalCheckFailedException" | Race condition on shadow update — Lambda retries up to 3 times | If persistent, check if multiple devices share the same node ID |
| No log entries at all | Lambda was never triggered — MQTT message did not reach SQS | Go to Step 3 |
Step 2: Verify the param data in DynamoDB
Go to AWS Console → DynamoDB → Tables → nodes_v3.
Click Explore items and query with:
- Partition key (
node_id): the node ID - Sort key (
node_attr_type):reported_params
What to look for:
- Entry exists — Check the
reported_params.datafield (a JSON string). Confirm it contains the expected parameter values. - Check
last_activity_timestamp— This Unix timestamp shows when the params were last updated. If it is older than expected, the device has not published recent updates. - No entry found — The device has never successfully published params, or the entry was never written. Continue to Step 3.
If the reported_params row is missing but the device has previously reported params, check the classic AWS IoT Device Shadow for this node: IoT Core → Manage → Things → <node_id> → Device Shadow (Classic). If data exists there, it will be automatically migrated to DynamoDB on the next GET API call.
Step 3: Check if the MQTT message reached the cloud
If there are no logs in the SetDeviceShadow Lambda, the device message is not making it through the pipeline. Check each stage:
Check the IoT Rule:
- Go to AWS Console → IoT Core → Message routing → Rules.
- Find the rule named
esp_set_params. - Confirm the rule is Enabled (not disabled).
- Check the rule's SQL statement — it must be:
select * as data, topic(2) as node_id, topic() as topic from 'node/+/params/local' - Click on the rule's Error action logs to see if any messages failed to be forwarded to SQS.
Verify the device is publishing on the correct topic:
The device must publish to exactly node/<node_id>/params/local. A common mistake is publishing to a slightly different topic (e.g., wrong node ID, extra slashes, or using the /init suffix when not initializing).
Use the AWS IoT Core → MQTT test client to monitor the topic:
- Go to IoT Core → Test → MQTT test client.
- Subscribe to
node/<node_id>/params/local. - Trigger a param update on the device and confirm the message appears here.
If no message appears in the MQTT test client, the device is either not connected or not publishing. See Debugging Node Connection Issues.
Check the SQS queue:
- Go to AWS Console → SQS →
esp-SetDeviceShadowSQS. - Check Messages available and Messages in flight.
- If messages are backed up (high count in flight), the Lambda may be throttled.
- Check the Dead Letter Queue for failed messages: if there is a DLQ attached, click Send and receive messages → Poll for messages to inspect failures.
Step 4: Check the GET API Lambda
If the data is correct in DynamoDB but the GET API still returns stale values, check the read path.
Go to CloudWatch → Logs Insights, select /aws/lambda/esp-NodeState, and run:
fields @timestamp, @message
| sort @timestamp desc
| filter @message like "<node_id>"
Look for:
"Getting node state failed"(error 102005) — DynamoDB read error on thereported_paramsrow.- Any errors fetching the user-node mapping — if the mapping is invalid, the API will reject the request before reading params.
No Param Data Found
Symptom: GET /user/nodes/params returns error 102006 — Device reported state not found.
This means the reported_params row in nodes_v3 is empty and no fallback data was found in the classic AWS IoT shadow either.
Step 1: Confirm the device has never reported params
Check DynamoDB nodes_v3 for the reported_params row (as described in Step 2 above). If it does not exist, the device has never successfully published to node/<node_id>/params/local.
Common reasons:
- The device firmware has not yet called the param reporting API.
- The device was registered recently and has not connected to the cloud yet.
- The device published but the IoT Rule
esp_set_paramswas disabled at the time.
Step 2: Check if the device is connected
Confirm the device is online by checking nodes_v3 for the all_attr row:
- Partition key: the node ID
- Sort key:
all_attr
Check the connected_status field — it must be true.
If the device has never connected, it has never been able to publish params. Refer to Debugging Node Connection Issues.
Step 3: Manually trigger a param refresh
If the device is online, you can trigger a param report by publishing a request to the device. Once the device responds with its current state on node/<node_id>/params/local, the Lambda will write the data and subsequent GET API calls will succeed.
App Param Update Not Reaching Device
Symptom: App calls PUT /user/nodes/params and gets a success response, but the device does not update.
Step 1: Confirm the API call reached the Lambda
Go to CloudWatch → Logs Insights, select /aws/lambda/esp-NodeState, and run:
fields @timestamp, @message
| sort @timestamp desc
| filter @message like "<node_id>"
Look for the PUT request log entry. Confirm no errors are present.
Step 2: Verify the MQTT publish succeeded
A success response from PUT /user/nodes/params means the Lambda published the param update to the MQTT topic node/<node_id>/params/remote. It does not confirm the device received it.
Use the AWS IoT Core → MQTT test client:
- Subscribe to
node/<node_id>/params/remote. - Make the PUT API call from the app.
- Confirm the message appears on this topic.
If the message does not appear, check Lambda IAM permissions for IoT Core iot:Publish.
Step 3: Confirm the device is subscribed to the remote topic
The device firmware must be subscribed to node/<node_id>/params/remote to receive param updates. If the device recently reconnected, it may have lost its subscriptions.
Check the node connection logs in /aws/lambda/esp-ConnectionNode for any recent reconnection events around the time the param update was sent:
fields @timestamp, @message
| sort @timestamp desc
| filter @message like "<node_id>"
A CLIENT_INITIATED_DISCONNECT or CONNECTION_LOST just before the param update would explain why the device did not receive it.
Step 4: Check node connectivity status in DynamoDB
Go to DynamoDB → nodes_v3 and query for the all_attr row:
- Partition key: the node ID
- Sort key:
all_attr
Check the connected_status field. If it shows true but the device is actually offline (stale status), the SET param API will publish to the MQTT topic but the device will not receive it.
The connectivity status is updated by IoT lifecycle events. If it is stale, the node connect/disconnect Lambda may have failed to update it. Check /aws/lambda/esp-ConnectionNode logs for recent events.
Node Showing Offline — Cannot Set Params
Symptom: GET /user/nodes/params (PUT) returns error 102001 — Node is offline.
The esp-NodeState Lambda checks the connected_status field in nodes_v3 before publishing to the device. If this field is false or missing, the API rejects the SET request.
Step 1: Verify the actual connectivity status
Check DynamoDB nodes_v3 all_attr row for connected_status as described above.
If connected_status is false:
- The device is genuinely offline — fix the device's internet/Wi-Fi connection.
- OR the status is stale (device is actually online but the field was not updated).
Step 2: Check for stale connectivity status
If the device appears online (you can ping it, or IoT Core shows it as connected) but connected_status in DynamoDB shows false:
- Go to CloudWatch → Logs Insights →
/aws/lambda/esp-ConnectionNode. - Filter by
<node_id>and check the most recent event. - If there is a recent
CONNECTevent but no corresponding DynamoDB update, the connection Lambda may have failed.
Fix: Wait for the device to reconnect — the connect event will update connected_status to true. If the status is permanently stale, check the esp-ConnectionNode Lambda for DynamoDB write errors.
Step 3: Check for device reconnect
If the device recently lost and re-established its MQTT connection, there may be a brief window where connected_status has not yet been updated to true. Wait 30–60 seconds after the device reconnects and retry the SET params call.
Intermittent Param Updates
Symptom: Params sometimes update correctly but occasionally are missed or delayed.
Check 1: SQS processing delays
High traffic or Lambda concurrency limits can cause messages to sit in the SQS queue before being processed. Check:
- Go to CloudWatch →
esp-SetDeviceShadowSQSand look at theApproximateAgeOfOldestMessagemetric. - If this value is consistently high (more than a few seconds), the Lambda is not keeping up with the message volume.
Fix: Increase the Lambda concurrency limit or check for throttling.
Check 2: DynamoDB conditional update retries
The esp-SetDeviceShadow Lambda uses a conditional write to prevent race conditions when multiple param updates arrive simultaneously. If two updates for the same node arrive within milliseconds of each other, the conditional check may fail.
The Lambda retries up to 3 times (SHADOW_UPDATE_RETRY_COUNT = 3). If all 3 retries fail, the update is silently dropped — no error is returned to SQS, so the message is not moved to a DLQ.
In CloudWatch for /aws/lambda/esp-SetDeviceShadow, search for:
fields @timestamp, @message
| filter @message like "ConditionalCheckFailedException" or @message like "<node_id>"
| sort @timestamp desc
| limit 100
If you see multiple ConditionalCheckFailedException entries for the same node, the device is publishing param updates too frequently. Consider reducing the update frequency in the device firmware.
Check 3: Malformed JSON payloads being silently dropped
If the device occasionally sends malformed JSON (e.g., during firmware updates or resets), the Lambda logs "Json is invalid" and silently drops the message without writing to DynamoDB.
In CloudWatch for /aws/lambda/esp-SetDeviceShadow, search for:
fields @timestamp, @message
| filter @message like "invalid" or @message like "Json"
| sort @timestamp desc
| limit 50
If found, check the device firmware's JSON serialization for edge cases (null values, special characters, encoding issues).
Check 4: IoT Rule error action
If the IoT Rule esp_set_params fails to route a message to SQS, the message is lost. Check the rule's Error action logs:
- Go to IoT Core → Message routing → Rules →
esp_set_params. - Check if an error action (CloudWatch Logs or SNS) is configured — if so, look for failed routing events.
Error Code Reference
| Error Code | Message | Likely Cause | Fix |
|---|---|---|---|
| 102001 | Node is offline | Device is disconnected or connected_status is false in DynamoDB | Check device connectivity; wait for reconnect |
| 102002 | Updating node state failed | DynamoDB write failure in esp-SetDeviceShadow Lambda | Check Lambda logs; check DynamoDB table health |
| 102003 | Getting node status failed | Cannot read connected_status from DynamoDB | Check nodes_v3 table; check Lambda IAM permissions |
| 102004 | Node does not exist | The node ID is not registered in nodes_v3 | Verify the node is registered; check nodes_v3 table |
| 102005 | Getting node state failed | DynamoDB read error on reported_params row | Check Lambda logs; check DynamoDB table health |
| 102006 | Device reported state not found | No reported_params row in DynamoDB and no classic shadow | Device has never published params; check device firmware |
| 102007 | Not able to find user node details | User-node mapping lookup failed | Verify the node is mapped to this user; check user-node mapping |
| 102008 | Maximum of 25 nodes can be set | Batch SET params request exceeds limit | Split into smaller batches (max 25 nodes per request) |
CloudWatch Log Groups Reference
| Log Group | When to Use |
|---|---|
/aws/lambda/esp-SetDeviceShadow | Primary log for param updates. Check here first when device params are not reflecting in the GET API |
/aws/lambda/esp-NodeState | Check GET and PUT /user/nodes/params API calls — both reading and writing params |
/aws/lambda/esp-SetDeviceShadowHttp | Check when device uses the HTTPS API (PUT /node/params/local) instead of MQTT to report params |
/aws/lambda/esp-LambdaSetParams | Check automation-triggered param updates |
/aws/lambda/esp-ConnectionNode | Check node connect/disconnect events — needed when connected_status appears stale |
DynamoDB Tables Reference
| Table | Row to Check | Key | What to Look For |
|---|---|---|---|
nodes_v3 | reported_params row | node_id (partition), reported_params (sort) | reported_params.data — the current param JSON; last_activity_timestamp — when last updated |
nodes_v3 | all_attr row | node_id (partition), all_attr (sort) | connected_status — must be true for SET params to work |