From c3cef782ac150f35bbf9bc23c2fc3b3b43ff9c7e Mon Sep 17 00:00:00 2001 From: Chirag Singhal <76880977+chirag127@users.noreply.github.com> Date: Tue, 7 Jul 2026 08:21:01 +0530 Subject: [PATCH] fix(compression): unknown engine names surface validationErrors instead of silently falling back (#6485) (#6506) Surface validationErrors for unknown stacked compression engines. Integrated into release/v3.8.46 with a TDD regression. --- CHANGELOG.md | 1 + .../services/compression/strategySelector.ts | 10 ++++- ...ion-unknown-engine-validation-6485.test.ts | 45 +++++++++++++++++++ 3 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 tests/unit/compression-unknown-engine-validation-6485.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 0949a4a235..26463bd75b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ ### 🐛 Bug Fixes +- **fix(compression):** a stacked-pipeline step naming an unregistered engine now surfaces a `validationErrors` entry instead of silently no-op'ing, so misconfigured pipelines are visible in the preview API (#6506 — thanks @chirag127). - **feat(usage):** add a **Codex reset-credit redemption** flow to the Provider Limits UI — a `useCodexResetCreditRedemption` hook + `/api/usage/codex-reset-credit` route + `codexResetCredits` lib let you redeem banked Codex reset credits from the quota card. Regression guard: `tests/unit/codex-reset-credits.test.ts`. (thanks @JxnLexn) - **feat(glm):** add **team-plan quota settings** for `glm-cn` connections — a dedicated `GlmTeamQuotaFields` form section (team quota id / limits) threaded through the Add/Edit connection modals, persisted via `providerSpecificData`, with the GLM usage service reading the team quota. Regression guards: `tests/unit/glm-team-quota.test.ts`, `provider-specific-data-schema.test.ts`. (thanks @hao3039032) - **feat(providers):** add **TinyFish** web-fetch/search support — a `tinyfish-fetch` executor + `/v1/web/fetch` route + MCP web-fetch tool, registered as a specialty-media provider with request-validation and a search-provider catalog entry. Regression guards: `tests/unit/executor-tinyfish-fetch.test.ts`, `web-fetch-handler.test.ts`, `mcp-web-fetch-tool.test.ts`, `provider-validation-tinyfish.test.ts`. (thanks @dtybnrj) diff --git a/open-sse/services/compression/strategySelector.ts b/open-sse/services/compression/strategySelector.ts index 0a7adbd0e8..4be701e205 100644 --- a/open-sse/services/compression/strategySelector.ts +++ b/open-sse/services/compression/strategySelector.ts @@ -839,7 +839,10 @@ function runStackedCompression( for (const step of steps) { const engine = getCompressionEngine(step.engine); - if (!engine) continue; + if (!engine) { + acc.validationErrors.add(`Unknown compression engine: "${step.engine}"`); + continue; + } // Respect the registry enabled flag: a step naming a disabled engine is skipped, so an // operator can turn an engine off (setEngineEnabled) without editing every pipeline. if (getEngineEntry(step.engine)?.enabled === false) continue; @@ -939,7 +942,10 @@ async function runStackedCompressionAsync( for (const step of steps) { const engine = getCompressionEngine(step.engine); - if (!engine) continue; + if (!engine) { + acc.validationErrors.add(`Unknown compression engine: "${step.engine}"`); + continue; + } // Respect the registry enabled flag (same as the sync loop) — keep both in lockstep. if (getEngineEntry(step.engine)?.enabled === false) continue; // T02: skip an engine whose breaker is OPEN (verbatim body kept — fail-open). Lockstep w/ sync. diff --git a/tests/unit/compression-unknown-engine-validation-6485.test.ts b/tests/unit/compression-unknown-engine-validation-6485.test.ts new file mode 100644 index 0000000000..8fee212c2e --- /dev/null +++ b/tests/unit/compression-unknown-engine-validation-6485.test.ts @@ -0,0 +1,45 @@ +import { test } from "node:test"; +import assert from "node:assert/strict"; + +import { applyStackedCompression } from "@omniroute/open-sse/services/compression/strategySelector"; + +// #6485 — a stacked pipeline step naming an engine which is not registered used to +// silently `continue`, so the caller had no signal that a configured step was a no-op. +// The fix records a `validationErrors` entry for each unknown engine while still +// fail-open (the request is not aborted). These tests pin that contract. +// +// Note: the unknown-engine path is only reachable via OBJECT steps ({ engine: "…" }). +// Bare-string steps are coerced to the "caveman" fallback by normalizePipelineStep, +// so they can never name an unknown engine. + +const body = { + messages: [ + { role: "user", content: "hello ".repeat(200) }, + { role: "assistant", content: "world ".repeat(200) }, + ], +}; + +test("unknown compression engine surfaces a validationErrors entry (sync)", () => { + const result = applyStackedCompression(body, [{ engine: "definitely-not-a-real-engine" }]); + const errors = result.stats?.validationErrors ?? []; + assert.ok( + errors.some((e) => e.includes("definitely-not-a-real-engine")), + `expected a validationErrors entry naming the unknown engine, got: ${JSON.stringify(errors)}` + ); +}); + +test("known + unknown mixed pipeline reports only the unknown engine (sync)", () => { + const result = applyStackedCompression(body, [ + { engine: "session-dedup" }, + { engine: "ghost-engine" }, + ]); + const errors = result.stats?.validationErrors ?? []; + assert.ok( + errors.some((e) => e.includes("ghost-engine")), + `expected the unknown engine to be reported, got: ${JSON.stringify(errors)}` + ); + assert.ok( + !errors.some((e) => e.includes("session-dedup")), + "a real engine must not be reported as unknown" + ); +});