Skip to content

feat(iot-client): report why the cloud refused a session token #29 - #29

Merged
sedawwk merged 1 commit into
tuya:masterfrom
xiongzh2000:fix/atop-error-on-session-token
Aug 27, 2026
Merged

feat(iot-client): report why the cloud refused a session token #29#29
sedawwk merged 1 commit into
tuya:masterfrom
xiongzh2000:fix/atop-error-on-session-token

Conversation

@xiongzh2000

Copy link
Copy Markdown

iot_client_get_session_token() returned OPRT_ATOP_BUSINESS_ERROR and nothing else, so a device could not tell apart the three refusals it actually meets:

GATEWAY_NOT_EXISTS device removed from the cloud
-> creds are dead, re-provision
CHILD_PRIVACY_AGREEMENT_REQUIRED agreement not signed yet
-> wait and retry; the user acts in the app
ISSUE_TOKEN_FAILED no AI agent configured for the product
-> retrying is pointless, say so

They need opposite responses, and only the caller knows the product, so the code has to reach it. Without it a device can only retry blindly -- which is wrong for two of the three, and hides a provisioning problem behind what looks like a flaky network.

The information was already parsed and then dropped: atop_base.c fills error_code/error_msg in atop_base_response_t and logs them, but atop_ai_token_get() carried only char *token out, and iot_client.c returned a bare int. atop.c now copies the rejection into ai_token_response_t, and a new iot_client_get_session_token_ex() hands it to the caller in an iot_atop_rejection_t.

Additive, not a break: iot_client_get_session_token() keeps its signature and becomes a wrapper passing NULL, and NULL means "don't care" -- the existing callers and the posix examples are untouched.

IOT_ATOP_ERROR_CODE_LEN / IOT_ATOP_ERROR_MSG_LEN move from iot_atop.h to iot_client.h beside the new struct, because iot_atop.h already includes iot_client.h and cannot be included back.

The mock gained a test-only hook: an agentCode of "reject:" makes it refuse with that errorCode. Real agent codes never contain a colon, and the token API had no rejection path to test against before -- the mock could only fail on an internal exception.

Verified: full build clean; ctest --timeout 180 15/15 (1 new suite); iot_session_token_test 7/7; run_leaks_check.sh reports 0 leaks. (The sanitizer build cannot run on this macOS/AppleClang host -- ASAN aborts in sanitizer_malloc_mac.inc before main, on existing suites too.)

iot_client_get_session_token() returned OPRT_ATOP_BUSINESS_ERROR and nothing
else, so a device could not tell apart the three refusals it actually meets:

  GATEWAY_NOT_EXISTS                device removed from the cloud
                                    -> creds are dead, re-provision
  CHILD_PRIVACY_AGREEMENT_REQUIRED  agreement not signed yet
                                    -> wait and retry; the user acts in the app
  ISSUE_TOKEN_FAILED                no AI agent configured for the product
                                    -> retrying is pointless, say so

They need opposite responses, and only the caller knows the product, so the
code has to reach it. Without it a device can only retry blindly -- which is
wrong for two of the three, and hides a provisioning problem behind what looks
like a flaky network.

The information was already parsed and then dropped: atop_base.c fills
error_code/error_msg in atop_base_response_t and logs them, but
atop_ai_token_get() carried only `char *token` out, and iot_client.c returned a
bare int. atop.c now copies the rejection into ai_token_response_t, and a new
iot_client_get_session_token_ex() hands it to the caller in an
iot_atop_rejection_t.

Additive, not a break: iot_client_get_session_token() keeps its signature and
becomes a wrapper passing NULL, and NULL means "don't care" -- the existing
callers and the posix examples are untouched.

IOT_ATOP_ERROR_CODE_LEN / IOT_ATOP_ERROR_MSG_LEN move from iot_atop.h to
iot_client.h beside the new struct, because iot_atop.h already includes
iot_client.h and cannot be included back.

The mock gained a test-only hook: an agentCode of "reject:<CODE>" makes it
refuse with that errorCode. Real agent codes never contain a colon, and the
token API had no rejection path to test against before -- the mock could only
fail on an internal exception.

Verified: full build clean; ctest --timeout 180 15/15 (1 new suite);
iot_session_token_test 7/7; run_leaks_check.sh reports 0 leaks.
(The sanitizer build cannot run on this macOS/AppleClang host -- ASAN aborts in
sanitizer_malloc_mac.inc before main, on existing suites too.)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@sedawwk
sedawwk merged commit 7e40c19 into tuya:master Aug 27, 2026
4 checks passed
heshaoqiong-tuya added a commit that referenced this pull request Aug 27, 2026
Applies convention 6 to the section that motivated it: 619 lines down to 139.

The entries were commit bodies pasted under a heading — motivation, rejected
alternatives, test counts and internal reasoning, several running past fifty
lines for one change. All of that is still in the commits, which is where a
reader who wants it looks. What is left here is what a reader acts on: the
symbol, the behaviour change, the migration step.

Most entries now carry a PR number. They could not be recovered from git
history because these landed as squash or rebase merges, which leave no
"Merge pull request #N" commit; the closed-PR list on GitHub has them, and each
attribution was confirmed against that PR's own commit list rather than
inferred from a branch name. Four entries carry none because they were pushed
straight to master with no PR to cite.

Two merged PRs turned out to have no entry at all, and are added:

- #21, the APP-confirmed OTA (protocol 15) callback — a public callback on both
  config structs.
- #23, the sizable ATOP response buffer — user-visible, since the sizes are set
  with -D.

Three defects the rewrite surfaced, all from entries being appended rather than
amended as the work continued:

- Added and Fixed each appeared twice, and the second Fixed held Added-type
  material (the generic ATOP call). Merged into one of each, in the order Keep
  a Changelog defines, since release notes are generated from those headings.
- The auto-connect default was documented both ways: Changed said it is now on
  by default, while the connect/disconnect entry still said "The default stays
  false". The later change never revisited the earlier entry.
- The music-play demo was credited to #15, which is the region-wire-codes fix.
  It is #12.

Two entries are dropped rather than shortened: the mqtt_abort_connect()
extraction and a test-only over-read fix. Convention 2 scopes the CHANGELOG to
what SDK users see, and neither is visible outside the repo.

Rebased onto five commits that landed meanwhile, whose entries are folded in at
the new length: the session-token-reason API (#29), the reset scope (#28), the
POSIX binary renaming, and audio_chat_demo's header-shadowing and device-VAD
fixes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@heshaoqiong-tuya heshaoqiong-tuya mentioned this pull request Aug 27, 2026
@sedawwk sedawwk changed the title feat(iot-client): report why the cloud refused a session token feat(iot-client): report why the cloud refused a session token #29 Aug 27, 2026
heshaoqiong-tuya added a commit that referenced this pull request Aug 27, 2026
Applies convention 6 to the section that motivated it: 619 lines down to 139.

The entries were commit bodies pasted under a heading — motivation, rejected
alternatives, test counts and internal reasoning, several running past fifty
lines for one change. All of that is still in the commits, which is where a
reader who wants it looks. What is left here is what a reader acts on: the
symbol, the behaviour change, the migration step.

Most entries now carry a PR number. They could not be recovered from git
history because these landed as squash or rebase merges, which leave no
"Merge pull request #N" commit; the closed-PR list on GitHub has them, and each
attribution was confirmed against that PR's own commit list rather than
inferred from a branch name. Four entries carry none because they were pushed
straight to master with no PR to cite.

Two merged PRs turned out to have no entry at all, and are added:

- #21, the APP-confirmed OTA (protocol 15) callback — a public callback on both
  config structs.
- #23, the sizable ATOP response buffer — user-visible, since the sizes are set
  with -D.

Three defects the rewrite surfaced, all from entries being appended rather than
amended as the work continued:

- Added and Fixed each appeared twice, and the second Fixed held Added-type
  material (the generic ATOP call). Merged into one of each, in the order Keep
  a Changelog defines, since release notes are generated from those headings.
- The auto-connect default was documented both ways: Changed said it is now on
  by default, while the connect/disconnect entry still said "The default stays
  false". The later change never revisited the earlier entry.
- The music-play demo was credited to #15, which is the region-wire-codes fix.
  It is #12.

Two entries are dropped rather than shortened: the mqtt_abort_connect()
extraction and a test-only over-read fix. Convention 2 scopes the CHANGELOG to
what SDK users see, and neither is visible outside the repo.

Rebased onto five commits that landed meanwhile, whose entries are folded in at
the new length: the session-token-reason API (#29), the reset scope (#28), the
POSIX binary renaming, and audio_chat_demo's header-shadowing and device-VAD
fixes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
heshaoqiong-tuya added a commit that referenced this pull request Aug 27, 2026
Applies convention 6 to the section that motivated it: 619 lines down to 139.

The entries were commit bodies pasted under a heading — motivation, rejected
alternatives, test counts and internal reasoning, several running past fifty
lines for one change. All of that is still in the commits, which is where a
reader who wants it looks. What is left here is what a reader acts on: the
symbol, the behaviour change, the migration step.

Most entries now carry a PR number. They could not be recovered from git
history because these landed as squash or rebase merges, which leave no
"Merge pull request #N" commit; the closed-PR list on GitHub has them, and each
attribution was confirmed against that PR's own commit list rather than
inferred from a branch name. Four entries carry none because they were pushed
straight to master with no PR to cite.

Two merged PRs turned out to have no entry at all, and are added:

- #21, the APP-confirmed OTA (protocol 15) callback — a public callback on both
  config structs.
- #23, the sizable ATOP response buffer — user-visible, since the sizes are set
  with -D.

Three defects the rewrite surfaced, all from entries being appended rather than
amended as the work continued:

- Added and Fixed each appeared twice, and the second Fixed held Added-type
  material (the generic ATOP call). Merged into one of each, in the order Keep
  a Changelog defines, since release notes are generated from those headings.
- The auto-connect default was documented both ways: Changed said it is now on
  by default, while the connect/disconnect entry still said "The default stays
  false". The later change never revisited the earlier entry.
- The music-play demo was credited to #15, which is the region-wire-codes fix.
  It is #12.

Two entries are dropped rather than shortened: the mqtt_abort_connect()
extraction and a test-only over-read fix. Convention 2 scopes the CHANGELOG to
what SDK users see, and neither is visible outside the repo.

Rebased onto five commits that landed meanwhile, whose entries are folded in at
the new length: the session-token-reason API (#29), the reset scope (#28), the
POSIX binary renaming, and audio_chat_demo's header-shadowing and device-VAD
fixes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
sedawwk pushed a commit that referenced this pull request Aug 27, 2026
* docs: require terse CHANGELOG entries with a PR number

The Unreleased section had drifted into commit bodies pasted under a heading:
single entries running fifteen-plus lines through motivation, rejected
alternatives and test counts. That is the right material, in the wrong file --
release notes are skimmed, and a reader who wants the reasoning goes to the
commit.

Convention 2 already said *when* an entry is needed; this says what it should
look like. `## [0.3.0]` is named as the reference because it is the last section
written that way: one line per change, sub-bullets only for specifics a reader
acts on.

Two things the section had also been losing: the PR number, which is the only
link from a one-line summary back to the reasoning, and the Added / Changed /
Fixed split, which release notes are generated from -- a fix landing under Added
is published as a feature.

No CHANGELOG entry for this commit, per convention 2: repo-internal, and this
file is explicitly excluded.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs(changelog): condense Unreleased to the new convention

Applies convention 6 to the section that motivated it: 619 lines down to 139.

The entries were commit bodies pasted under a heading — motivation, rejected
alternatives, test counts and internal reasoning, several running past fifty
lines for one change. All of that is still in the commits, which is where a
reader who wants it looks. What is left here is what a reader acts on: the
symbol, the behaviour change, the migration step.

Most entries now carry a PR number. They could not be recovered from git
history because these landed as squash or rebase merges, which leave no
"Merge pull request #N" commit; the closed-PR list on GitHub has them, and each
attribution was confirmed against that PR's own commit list rather than
inferred from a branch name. Four entries carry none because they were pushed
straight to master with no PR to cite.

Two merged PRs turned out to have no entry at all, and are added:

- #21, the APP-confirmed OTA (protocol 15) callback — a public callback on both
  config structs.
- #23, the sizable ATOP response buffer — user-visible, since the sizes are set
  with -D.

Three defects the rewrite surfaced, all from entries being appended rather than
amended as the work continued:

- Added and Fixed each appeared twice, and the second Fixed held Added-type
  material (the generic ATOP call). Merged into one of each, in the order Keep
  a Changelog defines, since release notes are generated from those headings.
- The auto-connect default was documented both ways: Changed said it is now on
  by default, while the connect/disconnect entry still said "The default stays
  false". The later change never revisited the earlier entry.
- The music-play demo was credited to #15, which is the region-wire-codes fix.
  It is #12.

Two entries are dropped rather than shortened: the mqtt_abort_connect()
extraction and a test-only over-read fix. Convention 2 scopes the CHANGELOG to
what SDK users see, and neither is visible outside the repo.

Rebased onto five commits that landed meanwhile, whose entries are folded in at
the new length: the session-token-reason API (#29), the reset scope (#28), the
POSIX binary renaming, and audio_chat_demo's header-shadowing and device-VAD
fixes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants