Skip to content

fix(proxy): classify Anthropic cache_control validation errors as 400, not 502 - #831

Merged
steventohme merged 2 commits into
mainfrom
fix/cache-control-dispatch-400
Jul 24, 2026
Merged

fix(proxy): classify Anthropic cache_control validation errors as 400, not 502#831
steventohme merged 2 commits into
mainfrom
fix/cache-control-dispatch-400

Conversation

@steventohme

Copy link
Copy Markdown
Collaborator

What

Anthropic cache_control validation errors (breakpoint overflow past the 4-cap, or a ttl=1h breakpoint ordered after a ttl=5m one) previously fell through ClassifyDispatchError to the generic "Upstream call failed" 502 handler — even though no upstream call was ever attempted. The router's own translate-layer validator (internal/translate/cache_control.go) rejects these before dispatch.

Now classified as a clean 400 (DispatchErrorAnthropicCacheControlInvalid), matching the pattern used for the other client-input error kinds. unwrapToSentinelMessage walks the error's wrap chain to strip the internal "emit body: " prefix while keeping the validator's own dynamic detail ("got 5, maximum is 4").

Why

Found by the router's real-Anthropic smoke suite (#828, smoke/cache_test.go's overflow-rejection scenario) on its first run against the real API — the in-process conformance suite never exercises what ClassifyDispatchError actually returns for this sentinel pair end to end. Split out of #828 per review — that PR is the test harness; this is the standalone production fix it caught.

Testing

  • go test ./internal/proxy/... — new unit tests assert 400 + IsClientError() + no leaked wrap prefix for both the overflow and TTL-ordering sentinels.
  • go build ./..., go vet ./..., gofmt -l . all clean.

…, not 502

Anthropic cache_control validation errors (breakpoint overflow past the
4-cap, or a ttl=1h breakpoint ordered after a ttl=5m one) previously fell
through ClassifyDispatchError to the generic "Upstream call failed" 502
handler — even though no upstream call was ever attempted. The router's
own translate-layer validator (internal/translate/cache_control.go)
rejects these before dispatch; the 502 path was both misleading (implies
an upstream failure that never happened) and didn't surface an
Anthropic-shaped error body for callers that parse one.

Now classified as a clean 400 (DispatchErrorAnthropicCacheControlInvalid),
matching the pattern used for the other client-input error kinds
(ErrRequestNotJSONObject, ErrInvalidRoutingKnobs, etc). unwrapToSentinelMessage
walks the error's wrap chain to the layer whose direct child is the sentinel
itself, so the client-facing message keeps the validator's dynamic detail
("got 5, maximum is 4") without leaking the internal "emit body: " wrap
prefix that every dispatch entry point adds.

Found by the router's real-Anthropic smoke suite (#828,
smoke/cache_test.go's overflow-rejection scenario) on its first run against
the real API — the in-process conformance suite never exercises what
ClassifyDispatchError actually returns for this sentinel pair end to end.

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

claude Bot commented Jul 24, 2026

Copy link
Copy Markdown

Claude finished @steventohme's task —— View job


  • Run git diff to inspect added comment lines
  • Analyze comment blocks for verbosity
  • Post review if needed

One 4-line godoc block on unwrapToSentinelMessage (lines 229–232 of dispatch_error.go) mostly restates the algorithm — filed as a committable suggestion in the review above.

@workweave-bot workweave-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Advisory only — comment-length nit. Won't block merge.

Comment thread internal/proxy/dispatch_error.go Outdated
@greptile-apps

greptile-apps Bot commented Jul 24, 2026

Copy link
Copy Markdown

T-Rex T-Rex Logs

What T-Rex did

  • T-Rex ran targeted proxy-cache-control tests and they exited with code 0.
  • T-Rex ran broader proxy package tests and they exited with code 0.
  • T-Rex executed the Go toolchain checks (go build, go vet, and gofmt); all commands exited with code 0 and gofmt produced no output.

View all artifacts

T-Rex Ran code and verified through T-Rex

Reviews (1): Last reviewed commit: "fix(proxy): classify Anthropic cache_con..." | Re-trigger Greptile

Co-authored-by: workweave-bot <bot@workweave.ai>
@claude

claude Bot commented Jul 24, 2026

Copy link
Copy Markdown

Claude finished @steventohme's task —— View job


  • Run git diff to inspect added comment lines
  • Analyze comment blocks for verbosity
  • Post review if needed

The 3-line godoc on unwrapToSentinelMessage (lines 229–231 of dispatch_error.go) that was flagged in the previous review has already been trimmed to 3 lines. No further comment-length issues found in this revision.

View job

@steventohme
steventohme merged commit 60f9f82 into main Jul 24, 2026
11 checks 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.

2 participants