Skip to content

docs: protocol spec v0.1.25.8 — UNIT_MISMATCH + BUDGET_NOT_FOUND#411

Merged
amavashev merged 3 commits intomainfrom
docs/protocol-spec-update-v0.1.25.8
Apr 11, 2026
Merged

docs: protocol spec v0.1.25.8 — UNIT_MISMATCH + BUDGET_NOT_FOUND#411
amavashev merged 3 commits intomainfrom
docs/protocol-spec-update-v0.1.25.8

Conversation

@amavashev
Copy link
Copy Markdown
Contributor

Summary

Aligns docs with three recent cycles-protocol-v0.yaml changes to the runtime error contract:

  • UNIT_MISMATCH (400) broadened from 2 → 4 operations. Previously documented only for commit + event; now also applies to reserve and decide when estimate.unit does not match any budget at the derived scopes but at least one of those scopes has a budget in a different unit.
  • BUDGET_NOT_FOUND (404) added as a distinct error code on POST /v1/reservations and POST /v1/events — docs previously conflated this with the generic NOT_FOUND used for missing reservations.
  • /v1/decide with missing budget returns 200 DENY reason_code=BUDGET_NOT_FOUND, not a 404 — consistent with decide's policy of surfacing budget-state conditions as decisions. UNIT_MISMATCH is still returned as 400 because it's a request-validity error.
  • Documents the details.expected_units field on UNIT_MISMATCH errors so clients can self-correct without a separate lookup.

Files changed

  • protocol/api-reference-for-the-cycles-protocol.md — reserve, decide, events error tables
  • protocol/error-codes-and-error-handling-in-cycles.md — split NOT_FOUND / BUDGET_NOT_FOUND sections, broadened UNIT_MISMATCH section, error count 15 → 16
  • protocol/understanding-units-in-cycles-usd-microcents-tokens-credits-and-risk-points.md — UNIT_MISMATCH description
  • how-to/budget-allocation-and-management-in-cycles.md — scope-lookup bullet + table
  • how-to/troubleshooting-and-faq.md — "first reservation" symptom heading
  • how-to/error-handling-patterns-in-python.md — retryable table
  • how-to/error-handling-patterns-in-cycles-client-code.md — retryable table

Spec references

Upstream commits on runcycles/cycles-protocol:

  • 577daf2 — add (d) decide to the normative UNIT_MISMATCH case list
  • beb00c1 — document 404 on /v1/reservations + /v1/events, rename details field available_unitsexpected_units
  • edb6b13 — broaden UNIT_MISMATCH normative description to cover reserve path

Test plan

  • npm run build — VitePress build completes without link errors
  • npm run dev — spot-check the 4 most-changed pages render correctly (error-codes-and-error-handling, api-reference, troubleshooting-and-faq, understanding-units)
  • Confirm no stale NOT_FOUND (404) language remains in reserve/event error tables

…_NOT_FOUND)

Updates docs to reflect three recent cycles-protocol-v0.yaml changes:

1. UNIT_MISMATCH (400) broadened from 2 to 4 operations
   Previously documented for commit + event. Spec normative text now
   covers reserve and decide as well — both can return 400 UNIT_MISMATCH
   when estimate.unit doesn't match any budget at the derived scopes but
   at least one of those scopes has a budget in a different unit.

2. BUDGET_NOT_FOUND (404) as a distinct error code
   POST /v1/reservations and POST /v1/events now explicitly return
   404 BUDGET_NOT_FOUND (distinct from NOT_FOUND for missing reservations)
   when no budget exists at any derived scope in any unit. Previously
   docs conflated both 404 cases under NOT_FOUND.

3. /v1/decide with missing budget returns 200 DENY, not 404
   Consistent with decide's policy of surfacing budget-state conditions
   as decisions. Reason code is BUDGET_NOT_FOUND. UNIT_MISMATCH is still
   returned as 400 because it's a request-validity error, not a
   budget-state condition.

Also documents the details.expected_units field (array of units for
which a budget does exist at the scope) so clients can self-correct
without a separate lookup.

Files updated:
- protocol/api-reference-for-the-cycles-protocol.md — reserve, decide,
  events error tables
- protocol/error-codes-and-error-handling-in-cycles.md — split NOT_FOUND
  section, new BUDGET_NOT_FOUND section, broadened UNIT_MISMATCH
  section, updated error count 15 -> 16
- protocol/understanding-units-in-cycles-*.md — broadened UNIT_MISMATCH
  description to cover all four operations, added BUDGET_NOT_FOUND note
- how-to/budget-allocation-and-management-in-cycles.md — scope lookup
  table and reservation rejection code
- how-to/troubleshooting-and-faq.md — updated "first reservation"
  symptom to use BUDGET_NOT_FOUND
- how-to/error-handling-patterns-in-python.md — retryable table
- how-to/error-handling-patterns-in-cycles-client-code.md — retryable
  table

Spec reference: cycles-protocol-v0.yaml commits
577daf2 (add decide to UNIT_MISMATCH list),
beb00c1 (document 404 on /v1/reservations + /v1/events),
edb6b13 (broaden UNIT_MISMATCH to reserve path).
…onCode enum

Follow-up to previous commit on this branch (ac2fbcb). Two follow-up
commits to cycles-protocol-v0.yaml corrected spec text that I had
faithfully transcribed but which was itself wrong:

1. runcycles/cycles-protocol ca1f672 — the runtime plane's ErrorCode
   enum only contains NOT_FOUND (not BUDGET_NOT_FOUND). The reference
   server returns `{"error": "NOT_FOUND", "message": "Budget not found
   for provided scope: ..."}` for the "no budget at any scope" case
   on non-dry reserve and /v1/events. BUDGET_NOT_FOUND never appears
   in the `error` field of 4xx runtime-plane responses.

2. runcycles/cycles-protocol ee88137 — the `reason_code` field on
   DecisionResponse and ReservationCreateResponse (dry_run only) is
   now a closed enum (DecisionReasonCode) with six values:
   BUDGET_EXCEEDED, BUDGET_FROZEN, BUDGET_CLOSED, BUDGET_NOT_FOUND,
   OVERDRAFT_LIMIT_EXCEEDED, DEBT_OUTSTANDING. BUDGET_NOT_FOUND IS a
   valid value here — on 200 DENY responses only, never on 4xx.

Changes:

- Reverted all `BUDGET_NOT_FOUND (404)` wire-code references to
  `NOT_FOUND (404)` with the message `"Budget not found for provided
  scope: ..."` carrying the specific reason.

- Merged the two 404 sections in protocol/error-codes-and-error-
  handling-in-cycles.md back into a single NOT_FOUND (404) section
  with two contexts (missing reservation, missing budget), matching
  the runtime plane's single-wire-code design.

- Added a new "Decision reason codes" section to protocol/error-
  codes-and-error-handling-in-cycles.md documenting the full
  DecisionReasonCode enum (6 values) with per-value semantics and
  the "forward compatibility" note from the spec.

- Updated protocol/api-reference-for-the-cycles-protocol.md /v1/decide
  section to document the reason_code enum inline in the Response
  (200 OK) description.

- Reverted error count in the Summary from 16 → 15 (BUDGET_NOT_FOUND
  is not a wire error code).

- Updated troubleshooting-and-faq.md heading back to "NOT_FOUND on
  first reservation" and added explanation of how the message field
  distinguishes missing-reservation from missing-budget.

- Updated error-handling-patterns docs (Python + client-code) to
  collapse the BUDGET_NOT_FOUND row back into NOT_FOUND, with the
  message-based distinction explained in the Meaning column.

- Broadened understanding-units doc to mention that /decide and
  dry-run surface the same condition as 200 DENY reason_code=
  BUDGET_NOT_FOUND.

Spec reference: cycles-protocol-v0.yaml commits ca1f672, ee88137
(both merged via runcycles/cycles-protocol#26).
…cide/dry-run pages

Follow-up verification pass against spec v0.1.25.8. Three fixes:

1. api-reference dry-run note was inaccurate. Said "4xx error codes
   above only apply when dry_run is false or omitted" — but request-
   validity errors (INVALID_REQUEST, UNIT_MISMATCH, UNAUTHORIZED,
   FORBIDDEN, IDEMPOTENCY_MISMATCH) still return 4xx on dry-run. Only
   budget-state conditions flip to 200 DENY. Narrowed the claim to the
   5 budget-state reason codes + the 404 "no budget" case.

2. how-decide-works-in-cycles page listed `reason_code` as a response
   field but didn't enumerate values. Added an inline 6-row table for
   the DecisionReasonCode enum (BUDGET_EXCEEDED, BUDGET_FROZEN,
   BUDGET_CLOSED, BUDGET_NOT_FOUND, OVERDRAFT_LIMIT_EXCEEDED,
   DEBT_OUTSTANDING) with the cross-reference to the
   error-codes-and-error-handling page for full semantics.

3. dry-run-shadow-mode-evaluation page similarly had a "reason_code
   on DENY" section that didn't list values. Added the same enum
   reference + the note that dry-run `BUDGET_NOT_FOUND` corresponds
   to the same underlying condition non-dry reserve surfaces as HTTP
   404 with error=NOT_FOUND.
@amavashev amavashev merged commit ba6265f into main Apr 11, 2026
1 check passed
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