From 1846401f8150f5d7c179f876f54d8724a4dcbb67 Mon Sep 17 00:00:00 2001 From: teamchong <25894545+teamchong@users.noreply.github.com> Date: Sat, 18 Jul 2026 12:38:08 -0400 Subject: [PATCH] fix(logs): tag compression skip reasons as savings:skip(...) (#114) (#115) Two issues (#114, #115) reported that pxpipe was "causing" 429 unsupported_model and 529 overloaded errors. It wasn't -- but our log output was actively misleading people into believing it was, and this change fixes that. Background: for every proxied request, both the node and worker log paths print a one-line summary: POST /v1/messages -> 429 (755ms) unsupported_model cache_read=0 The third field is a tag describing what pxpipe's compression layer did with the request. When compression ran, it reads something like "compressed 30761ch -> 4img/98060B". When compression was skipped, it printed the internal skip reason -- bare, with no prefix. The problem is where that bare reason lands: immediately after the HTTP status, exactly where a reader expects the error explanation to appear. So when Anthropic returned a 429 whose body also happened to contain "unsupported_model", the log line read as "pxpipe decided my model is unsupported and returned a 429". In reality the two strings merely coincided: pxpipe's "unsupported_model" skip reason means "this model is not in the PXPIPE_MODELS savings scope (default: Fable 5 only), so the request was passed through byte-for-byte untouched", while the 429 body was Anthropic's own upstream response, forwarded verbatim. pxpipe never generates 429s or 529s itself. The fix is to wrap the skip reason so it cannot be read as an error cause: Before: POST /v1/messages -> 429 (755ms) unsupported_model After: POST /v1/messages -> 429 (755ms) savings:skip(unsupported_model) Applied to both log call sites (src/node.ts and src/worker.ts -- the latter is what shows up in `wrangler tail`). The "compressed ..." tag for transformed requests is unchanged, and an empty reason still prints nothing. Log-only, no behavior change; all 61 existing tests pass. Fixes #114 Fixes #115 --- src/node.ts | 4 +++- src/worker.ts | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/node.ts b/src/node.ts index e01a1f6..e0839b5 100644 --- a/src/node.ts +++ b/src/node.ts @@ -1043,7 +1043,9 @@ async function main(): Promise { const extraTag = extra.length > 0 ? ` (${extra.join(' ')})` : ''; const tag = e.info?.compressed ? `compressed ${e.info.origChars}ch → ${e.info.imageCount}img/${e.info.imageBytes}B${extraTag}` - : (e.info?.reason ?? ''); + : e.info?.reason + ? `savings:skip(${e.info.reason})` + : ''; const cacheRead = e.usage?.cache_read_input_tokens ?? 0; const inputTokens = e.usage?.input_tokens ?? 0; const usageTag = diff --git a/src/worker.ts b/src/worker.ts index dade6ec..b9d8058 100644 --- a/src/worker.ts +++ b/src/worker.ts @@ -148,7 +148,9 @@ export default { // shows up in `wrangler tail`). const tag = e.info?.compressed ? `compressed ${e.info.origChars}ch → ${e.info.imageCount}img/${e.info.imageBytes}B` - : (e.info?.reason ?? ''); + : e.info?.reason + ? `savings:skip(${e.info.reason})` + : ''; const cacheRead = e.usage?.cache_read_input_tokens ?? 0; console.log(`${e.method} ${e.path} → ${e.status} (${e.durationMs}ms) ${tag} cache_read=${cacheRead}`);