fix(retry): degrade optional params on [1210] invalid input 400s (#190) - #191
Conversation
|
Nice one @Fahad090NP, the classifier and tests look clean. one thing before I merge though. the ladder doesn't actually run end-to-end right now. the JSDoc says "one per engine retry attempt", which is why I'm asking for the fix rather than a doc edit. could you wrap the analyze → patch → retry in let response = await fetchWithTransientRetry(payload);
let consumedErrorBody: string | undefined;
for (let patchAttempt = 0; patchAttempt < 3 && response.status === 400; patchAttempt++) {
const errorDetail = consumedErrorBody ?? (await response.text());
const parsedBody = JSON.parse(rawPayload) as Record<string, unknown>;
const patch = analyzeHttp400ForRetry(errorDetail, parsedBody);
if (!patch) break;
options.output?.appendLine(`[retry] HTTP 400 recoverable: ${patch.reason}. Retrying with patched body…`);
payload = JSON.stringify(patch.body);
rawPayload = payload; // keep them in sync for the next iteration
response = await fetchWithTransientRetry(payload);
consumedErrorBody = response.status === 400 ? await response.text() : undefined;
}(key detail: re-analyze has to run against the patched body, so a couple of notes while you're in there:
happy to merge once the loop is in. thanks for picking this up. |
ox-alpha-free rejects requests with '[1210] Invalid API parameter … invalid input' without naming the offending parameter. Add a patchInvalidInput classifier that strips optional parameters one per retry attempt, least-likely-to-matter first: 1. stream_options (usage accounting hint) 2. temperature (fall back to model default) 3. image parts (some models reject data-URL images despite metadata) Each patch only fires when the parameter is present, so the sequence stops once everything optional is gone and real failures surface. 5 new unit tests.
…view #191) The [1210] degradation ladder never ran end-to-end: the engine called analyzeHttp400ForRetry once and retried once, so only the first patch (stream_options) was ever applied within a single request. Wrap the block in a loop (MAX_400_PATCH_ATTEMPTS=3): - each iteration re-analyzes the latest 400 body against the LATEST patched payload (rawPayload kept in sync) - consumedErrorBody carried between iterations so bodies aren't re-read - [http-error-body] log stays, now inside the loop - loop exits when status != 400 or the analyzer has no patch left
a495277 to
2cd7342
Compare
|
Loop is in.
Also rebased onto current |
|
Nice @Fahad090NP, the loop looks right, merging now. |
- Bump package.json version to 0.7.1 - CHANGELOG: move Unreleased entries to [0.7.1] — 2026-08-26 - docs: add issue doc 82 for Muse Spark 1.2 vision fallback (#183) - docs: update issue docs 80/81 status to Solved + Landed refs - docs: sync devlog with post-0.7.0 hotfix wave (PR #188, #189, #191) - remove duplicate heading in devlog
Fixes #190
Summary
ox-alpha-freerejects requests with HTTP 400:The gateway gives no hint which parameter is invalid, and the reporter's payload was 1.98 MB — well-formed otherwise. Nothing in our request builder is obviously wrong for this model (it's a fallback-family model, so no thinking fields are even sent), which means one of the optional parameters we always include isn't accepted by this upstream.
Fix
New
patchInvalidInputclassifier insrc/retry.ts: when a 400 body matches[1210] … invalid input / invalid API parameter, the retry degrades the request by stripping optional parameters one per attempt, least-likely-to-matter first:stream_options(usage accounting hint — pure nicety)temperature(fall back to the model's own default)Each patch only fires when the parameter is actually present, so once everything optional is gone the sequence stops and real failures surface normally — no infinite retries, and no masking of genuine errors after degradation is exhausted.
This mirrors how #171 (completion-cap) and #109 (context overflow) self-heal: use the engine's existing 400-retry to adapt to upstream constraints we can't know ahead of time.
Verification
npm run lintall 7 checks green.src/test/retry.test.ts: each degradation step, image-part stripping while preserving text, exhaustion → undefined, and no-fire for unrelated 400s.Fi Amanillah