You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Shipped in v0.2.3 (PR #9, closes #8), the enrich_budget_not_found(err, unit) helper in `src/client.rs` rewrites `Error::Api.message` when the server returns a misleading `404 NOT_FOUND "Budget not found for provided scope: "` on wrong-unit reserve/decide/event calls. It was a workaround for the server's out-of-spec 404 on these paths.
The server-side root-cure is runcycles/cycles-server#79 (v0.1.25.6, branch `fix/reserve-unit-mismatch`). Once merged and deployed, wrong-unit requests return structured `400 UNIT_MISMATCH` with `details.{scope,requested_unit,expected_units}` directly from the server — no client-side enrichment needed.
Why this is a tracking issue, not an immediate fix
The workaround is forward-compatible with the server fix:
Wrong-unit case → server now returns `400 UNIT_MISMATCH` → the enrichment predicate (`status == 404 && code == NotFound && message.starts_with("Budget not found for provided scope")`) does not match → passes through unchanged. Users get the structured `details` from the server.
Truly-missing case → server still returns `404 NOT_FOUND` with the same marker → enrichment still fires and remains helpful (tells the user the unit they sent).
So there is no urgency. The workaround can stay indefinitely without harm. This issue is just a note to retire it cleanly in a future minor bump.
Scope of work (0.3.0)
When the fleet is known to be on `cycles-server` ≥ 0.1.25.6:
`src/client.rs:17` — drop the `use crate::models::enums::Unit;` import if it becomes unused.
`src/client.rs` — remove the four `.map_err(|e| enrich_budget_not_found(e, …))` chains on `create_reservation`, `create_reservation_with_metadata`, `decide`, `create_event`.
`src/client.rs::tests` — remove the 5 unit tests: `enrich_budget_not_found_adds_unit_hint`, `_uses_wire_format_for_unit`, `_ignores_non_matching_messages`, `_ignores_non_404_errors`, `_passes_through_other_error_kinds`.
Add typed surface for the new `400 UNIT_MISMATCH` details payload:
Extend `Error::Api.details` usage OR add a dedicated `Error::UnitMismatch { scope, requested_unit, expected_units, request_id }` variant. Prefer the dedicated variant — it parallels `Error::BudgetExceeded` and lets users pattern-match without parsing `serde_json::Value`.
Parse `details.scope`, `details.requested_unit`, `details.expected_units` from the server's `ErrorResponse` in `parse_error_response` when `code == Some(UnitMismatch) && status == 400`. Spec says the details payload is `SHOULD`, so make all three fields tolerant (`Option<…>`).
Add `Error::expected_units()` accessor and update `Error::is_budget_exceeded()`-style helpers if needed.
Tests:
New wiremock integration test: reserve → `400 UNIT_MISMATCH` with full `details` payload → assert the new error variant is populated with `scope`, `requested_unit`, `expected_units`.
Regression test: a `400 UNIT_MISMATCH` without `details` (legacy commit path per spec line 56's SHOULD wording) still deserializes cleanly with `None` fields.
Regression test: a `400 UNIT_MISMATCH` for a commit-vs-reservation mismatch continues to work (unchanged code path, but worth a fixture).
Docs:
Remove the `(scope, unit)` invariant callouts from `src/models/common.rs`, `src/lifecycle.rs`, `examples/with_cycles_usage.rs`, and `README.md` — OR rewrite them to describe the new typed error surface instead of the old 404 confusion.
`CHANGELOG.md` 0.3.0 entry: `Removed` the workaround (note it as a breaking change only if the `Error` variant changes — in which case bump to 0.3.0 minor as planned, not a patch).
`AUDIT.md` 0.3.0 section referencing this issue and cross-linking cycles-server#79.
Version: 0.2.x → 0.3.0 in `Cargo.toml` (breaking if `Error` gains a new non-exhaustive variant users were matching exhaustively; otherwise 0.2.4 is fine).
Pre-conditions for merging this
cycles-server#79 merged and released as 0.1.25.6 (or later).
All production deployments of `cycles-server` we know about are on ≥ 0.1.25.6. For self-hosted users, the 0.3.0 release notes should state the minimum server version.
`cycles-protocol-v0.yaml` lines 53-64 (branch `fix/reserve-unit-mismatch`) merged to main — the `400 UNIT_MISMATCH` on reserve is then spec-normative.
Non-goals
Not a breaking change to the public `CyclesClient` API surface (methods, builders, config). Only `Error` variants may grow.
Not touching commit/release/extend/list/get — those were never affected by the workaround.
Not retiring the `(scope, unit)` documentation invariant — the invariant is still true (budgets are still indexed by `(scope, unit)`), only the diagnostic pathway changes.
Low priority — the workaround is forward-compatible and causes no harm. Pick up when working on an unrelated 0.3.0 change, or if the `Error` enum is being restructured for another reason.
Context
Shipped in v0.2.3 (PR #9, closes #8), the
enrich_budget_not_found(err, unit)helper in `src/client.rs` rewrites `Error::Api.message` when the server returns a misleading `404 NOT_FOUND "Budget not found for provided scope: "` on wrong-unit reserve/decide/event calls. It was a workaround for the server's out-of-spec 404 on these paths.The server-side root-cure is runcycles/cycles-server#79 (v0.1.25.6, branch `fix/reserve-unit-mismatch`). Once merged and deployed, wrong-unit requests return structured `400 UNIT_MISMATCH` with `details.{scope,requested_unit,expected_units}` directly from the server — no client-side enrichment needed.
Why this is a tracking issue, not an immediate fix
The workaround is forward-compatible with the server fix:
So there is no urgency. The workaround can stay indefinitely without harm. This issue is just a note to retire it cleanly in a future minor bump.
Scope of work (0.3.0)
When the fleet is known to be on `cycles-server` ≥ 0.1.25.6:
Pre-conditions for merging this
Non-goals
Related
Low priority — the workaround is forward-compatible and causes no harm. Pick up when working on an unrelated 0.3.0 change, or if the `Error` enum is being restructured for another reason.