Skip to content

fix(errors): full agent-native APIError envelope + Capabilities() + live prod integration test#20

Merged
mastermanas805 merged 4 commits into
masterfrom
fix/sdk-apierror-full-envelope
Jun 10, 2026
Merged

fix(errors): full agent-native APIError envelope + Capabilities() + live prod integration test#20
mastermanas805 merged 4 commits into
masterfrom
fix/sdk-apierror-full-envelope

Conversation

@mastermanas805

Copy link
Copy Markdown
Member

What

Restores the full agent-native error envelope on the SDK's `APIError` and adds a typed `Capabilities()` surface, plus a build-tagged live integration test that proves the fix against PROD.

Fix (commit f9f1ab6)

  • `APIError` now captures every field of the canonical instanode.dev error envelope: `agent_action`, `error_code`, `upgrade_url`, `retry_after_seconds`, `request_id` (previously dropped).
  • `(*APIError).CanonicalCode()` — returns the finer-grained `error_code` when present, falling back to the `error` category, so callers key off a stable machine code.
  • `APIErrorEnvelopeKeys` registry + `TestAPIError_EnvelopeKeysAllHaveAHome` — a registry-iterating test asserts every envelope key has a tagged home on the struct, so a future API field can't silently drop.
  • `Client.Capabilities(ctx)` — typed read of the public `GET /api/v1/capabilities` tier matrix (with a `Raw` escape hatch for forward compat).

Live prod integration test (commit 2f35668)

  • `instant/integration_prod_test.go` (`//go:build integration`) pins PROD `https://api.instanode.dev\` and exercises read-only surfaces:
    • unauthenticated `GET /api/v1/resources` → asserts the SDK's `APIError` carries `agent_action` + `error_code` (via `CanonicalCode`) + `upgrade_url`, proving the envelope fix against the real response.
    • `Capabilities()` → asserts the live tier matrix decodes and returns tiers.
  • Skips cleanly when prod is unreachable from the runner.
  • Wired into `ci.yml` as a new `integration` job (`go test -tags integration ... -run TestProdIntegration`).

Verification (against live prod)

```
TestProdIntegration_APIErrorEnvelope — PASS
status=401 code="unauthorized" error_code="missing_credentials"
agent_action="...log in at https://instanode.dev/login..." upgrade_url="https://instanode.dev/login"
TestProdIntegration_Capabilities — PASS
7 tiers: anonymous, free, hobby, hobby_plus, pro, growth, team
```

Gate green: `go build ./... && go test ./... -short`. Build-tagged build/vet also clean.

🤖 Generated with Claude Code

mastermanas805 and others added 4 commits June 10, 2026 08:55
P0: APIError dropped most of the API's canonical error envelope. The api
replies to every 4xx/5xx with {ok, error, error_code, message, agent_action,
upgrade_url, retry_after_seconds, request_id}, but APIError captured only
error + message — silently dropping agent_action, error_code, upgrade_url,
retry_after_seconds, and request_id. And Code was tagged json:"error" so it
held the category ("unauthorized"), not the canonical machine code
("missing_credentials" in error_code). This defeated the agent-native
contract: an agent had no next-step (agent_action), no upgrade path
(upgrade_url), no precise code to branch on, no request_id for support.

Fix:
- Add tagged fields ErrorCode, AgentAction, UpgradeURL,
  RetryAfterSeconds (*int, to distinguish "retry in 0s" from "do not
  retry"), and RequestID to APIError. Code/Message retained for back-compat.
- Add APIError.CanonicalCode() — prefers ErrorCode, falls back to Code — so
  callers branch on the canonical machine code.
- Fold agent_action + upgrade_url into Error() so logs are actionable; the
  legacy "(code): message" shape is unchanged when neither is present.
- Exported APIErrorEnvelopeKeys registry + rule-18 registry tests
  (TestAPIError_EnvelopeKeysAllHaveAHome / …RegistryIsComplete /
  …FullEnvelopeRoundTrips / …FullEnvelopeOverHTTP) so a future envelope key
  can't silently drop. Existing APIError tests (DecodedFromBody, RawFallback,
  RawOnly, Error string) still pass — backward compatible.

Add Client.Capabilities(ctx) → GET /api/v1/capabilities, returning the typed
tier matrix (*Capabilities / []TierCapabilities) plus a Raw map escape hatch
for forward compatibility. Six provider doc comments referenced this endpoint
with no method to call it; this is the first. Public/unauthenticated — works
in anonymous mode. Covered by capabilities_test.go.

Gate: go build ./... && go test ./... -short — green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…o CI

Add a build-tagged (//go:build integration) live integration test that hits
PROD https://api.instanode.dev and proves the agent-native error envelope fix
against the real API:

- unauthenticated GET /api/v1/resources → assert APIError carries
  agent_action + error_code (CanonicalCode) + upgrade_url
- Capabilities() → assert the public tier matrix decodes and returns tiers

Both are read-only / side-effect-free and skip cleanly when prod is
unreachable from the runner. Wired into ci.yml as a new 'integration' job
(go test -tags integration ... -run TestProdIntegration).

Verified passing against prod: status=401 missing_credentials,
agent_action + upgrade_url populated; 7 tiers returned.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
typos v1.47 flags RTO/rto (Recovery Time Objective) as 'should be TO'.
These are real durability-SLO terms used by the Capabilities() API.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
diff-cover flagged capabilities.go:120-125 (re-encode + decode error paths).
Re-encoding a decoded map can't fail, so decode the raw bytes directly into
both the typed struct and Raw — removing the dead branch — and add a
decode-error test for the type-mismatch path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@mastermanas805
mastermanas805 merged commit fff3fe5 into master Jun 10, 2026
11 checks passed
@mastermanas805
mastermanas805 deleted the fix/sdk-apierror-full-envelope branch June 10, 2026 03:59
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.

1 participant