Skip to content

fix(http): raise TurnkeyActivityError when an activity does not complete - #21

Merged
figitaki merged 7 commits into
mainfrom
carey/activity-errors
Oct 18, 2026
Merged

figitaki merged 7 commits into
mainfrom
carey/activity-errors

Conversation

@figitaki

@figitaki figitaki commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Typed activity methods now raise a typed error when an activity does not complete. Before this change, they raised a pydantic ValidationError.

Stacks on #20. Merge #20 first.

Problem

_activity polls the activity. If the status is not ACTIVITY_STATUS_COMPLETED, it returns response_type(activity=activity). The response models declare result fields as required, so pydantic rejects the response.

Reproduction: call client.create_wallet(...) with a policy that denies the activity. The call raises ValidationError: walletId Field required. The activity id and the failure reason are lost.

Callers cannot tell a policy denial from a bug. Also, pydantic's ValidationError is a subclass of ValueError, so a broad except ValueError hid the problem.

New behavior

New errors in turnkey_sdk_types:

  • TurnkeyActivityError(TurnkeyError) with activity_id, activity_status, activity_type, and failure (the failure message from Turnkey, if any). The names match TurnkeyActivityError in @turnkey/http.
  • TurnkeyActivityConsensusNeededError(TurnkeyActivityError).
  • New TurnkeyErrorCodes: ACTIVITY_FAILED, ACTIVITY_CONSENSUS_NEEDED, ACTIVITY_RESULT_MISSING.
Status after polling Result
COMPLETED with the expected result Returns the typed response, as before
COMPLETED without the expected result key Raises TurnkeyActivityError (code ACTIVITY_RESULT_MISSING). The message names the expected key
CONSENSUS_NEEDED Raises TurnkeyActivityConsensusNeededError
FAILED Raises TurnkeyActivityError with the failure message
REJECTED Raises TurnkeyActivityError
Not terminal when polling stops (CREATED, PENDING, AUTHENTICATORS_NEEDED) Raises TurnkeyActivityError with the last status seen and the activity id
Any status, send_signed_request without response_type Returns the raw payload with the activity. No error. Unchanged

send_signed_request with an ACTIVITY request and a response_type uses the same status check. Without a response_type, send_signed_request does not raise. It returns the raw payload for every status, as before, so callers can inspect a FAILED, REJECTED, or CONSENSUS_NEEDED activity. When the activity completes, the raw payload also has the result fields. _activity_decision does not change. Its response types have only activity, and a REJECTED status is the expected result of a reject call.

Generic path fix

send_signed_request looked for result attributes whose names end in Result. Versioned keys such as createSubOrganizationResultV7 were skipped. Now it takes the first result field that is set on the activity result model. Pinned and latest result keys both work.

Breaking change

Callers of typed activity methods that caught ValidationError (or ValueError) for non-completed activities now get TurnkeyActivityError. The same applies to send_signed_request with a response_type. The raw path (send_signed_request without a response_type) is unchanged. It still returns the activity for every status.

Changes

  • Generator change in codegen/http/generate_http.py, then make generate. The regenerated diff changes only _raise_for_activity_status (new), _activity, and send_signed_request. No endpoint method bodies change.
  • patch changeset for http and sdk-types.
  • README section "Handling activity outcomes".

Testing

  • New offline tests in packages/http/tests/test_activity_errors.py. They mock requests.post and use a stub stamper. They need no credentials and do not use the conftest.py fixtures. They cover a completed activity, FAILED, REJECTED, CONSENSUS_NEEDED, a polling timeout, a completed activity with a missing result key, a createSubOrganizationResultV7 result through send_signed_request with and without a response_type, a failed activity through send_signed_request with a response_type, and the raw path with FAILED and CONSENSUS_NEEDED (no error, status and failure intact).
  • All 11 tests pass.
  • A script checked that all 114 result keys in the generated client are fields of v1Result.
  • make typecheck passes. ruff format --check . passes.
  • The live tests in test_activities.py and test_queries.py were not run.

🤖 Generated with Claude Code

emostov
emostov previously approved these changes Oct 18, 2026
@figitaki
figitaki marked this pull request as ready for review October 18, 2026 00:08
Base automatically changed from carey/codegen-bump-2026-10 to main October 18, 2026 00:09
@figitaki
figitaki dismissed emostov’s stale review October 18, 2026 00:09

The merge-base changed after approval.

figitaki and others added 7 commits October 17, 2026 17:09
…sNeededError

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…mplete

The emitted _activity helper returned response_type(activity=activity)
for any status other than COMPLETED. Response models declare result
fields as required, so callers got a pydantic ValidationError and lost
the activity id and failure reason.

The generic send_signed_request path matched result keys that end in
"Result", so versioned keys such as createSubOrganizationResultV7 were
skipped. It now takes the result field that is set.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
send_signed_request now raises TurnkeyActivityError only when a
response_type is passed. Without one, it returns the raw payload for
every status, as before, so callers can inspect a failed, rejected, or
consensus-needed activity. Completed results still unpack, including
versioned keys.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@figitaki
figitaki force-pushed the carey/activity-errors branch from 62bdc26 to a7d36af Compare October 18, 2026 00:09
@figitaki
figitaki merged commit c7d9a1c into main Oct 18, 2026
5 checks passed
@figitaki
figitaki deleted the carey/activity-errors branch October 18, 2026 01:08
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.

2 participants