Skip to content

QVAC-20529 fix[api]: friendly, field-level validation errors for user input#2618

Merged
arun-mani-j merged 2 commits into
tetherto:mainfrom
arun-mani-j:simpleErrors
Jun 17, 2026
Merged

QVAC-20529 fix[api]: friendly, field-level validation errors for user input#2618
arun-mani-j merged 2 commits into
tetherto:mainfrom
arun-mani-j:simpleErrors

Conversation

@arun-mani-j

Copy link
Copy Markdown
Contributor

🎯 What problem does this PR solve?

  • A single mistyped modelConfig key made loadModel reject with a multi-KB raw
    ZodError JSON blob that leaked into the renderer UI — a wall of JSON instead of a
    readable message.
  • Causes: client schema parses threw raw ZodErrors (whose .message is serialized
    JSON), and the modelType/config schemas are z.unions, so even when caught Zod
    reported a generic "Invalid input" across every branch instead of the actual field.

📝 How does it solve it?

  • formatZodError (wraps Zod v4's built-in z.prettifyError) is the single error
    formatter; replaces the duplicated error.issues.map(...) blocks.
  • Every client input parse is wrapped (parseClientInput) and throws a typed
    RequestValidationFailedError (code 50010) with a prettified, non-JSON message;
    createErrorResponse prettifies any ZodError on the wire. The UI never receives
    raw serialized ZodError JSON.
  • The modelType-keyed load-model unions and the ttsEngine-keyed tts/translate
    config unions became z.discriminatedUnions, and parseRequest re-validates a
    failed request against the member owning its type — so errors name the offending
    field instead of collapsing to "Invalid input".
  • Scope: only user-provided input gets friendly messages; malformed worker→client
    responses (SDK bugs, dev-facing) intentionally stay raw.

🧪 How was it tested?

  • test/unit/zod-error-formatting.test.ts (14 tests): no-JSON guarantees,
    loadModel/tts field-level errors, per-op type dispatch (embed/transcribe/translate)
    through the real send() path. bun run test:unit passes; bun run lint clean.
  • Manual, via a config typo in examples/quickstart.ts + bun examples/quickstart.ts:
Input Before After
top-level typo modelConfg: {...} ZodError: [ { "code": "invalid_union", "errors": [[ … ]] } ] (multi-KB blob) Invalid request:
✖ Unrecognized key: "modelConfg"
nested typo modelConfig: { dtx_size: 4096 } same multi-KB union blob Invalid request:
✖ Unrecognized key: "dtx_size"
→ at modelConfig

🔌 API Changes

New exported error class RequestValidationFailedError (code 50010) for catching
invalid-input rejections:

import { loadModel, RequestValidationFailedError, LLAMA_3_2_1B_INST_Q4_0 } from "@qvac/sdk";

try {
  await loadModel({ modelSrc: LLAMA_3_2_1B_INST_Q4_0, modelConfig: { dtx_size: 4096 } });
} catch (err) {
  if (err instanceof RequestValidationFailedError) {
    console.error(err.message);
    // Invalid request:
    // ✖ Unrecognized key: "dtx_size"
    //   → at modelConfig
  }
}

@arun-mani-j arun-mani-j requested review from a team as code owners June 16, 2026 07:21
@arun-mani-j arun-mani-j added test-e2e-smoke Triggers smoke e2e test suite [Currently SDK-only] verified Authorize secrets / label-gate in PR workflows labels Jun 16, 2026
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Tier-based Approval Status

**PR Tier:** TIER1

**Current Status:** ✅ APPROVED

**Requirements:**
- 1 Team Member approval ✅ (1/1)
- 1 Team Lead OR Management approval ✅ (1/1)



---
*This comment is automatically updated when reviews change.*

@arun-mani-j arun-mani-j added test-e2e-smoke Triggers smoke e2e test suite [Currently SDK-only] and removed test-e2e-smoke Triggers smoke e2e test suite [Currently SDK-only] labels Jun 16, 2026
@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

QVAC E2E — ios — ✅ all tests passed (84/98, 1021s)

Config: suite=smoke · filter=(none) · exclude=(none)
View run · Artifacts: reports · Device Farm logs

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

QVAC E2E — windows — ✅ all tests passed (98/98, 420s)

Config: suite=smoke · filter=(none) · exclude=(none)
View run · Artifacts: reports

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

QVAC E2E — linux⚠️ no results

Config: suite=smoke · filter=(none) · exclude=(none)
View run

The test job did not produce a results artifact. Check the run for job-level failures.

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

QVAC E2E — android — ✅ all tests passed (86/98, 2553s)

Config: suite=smoke · filter=(none) · exclude=(none)
View run · Artifacts: reports · Device Farm logs

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

QVAC E2E — macos — ✅ all tests passed (98/98, 285s)

Config: suite=smoke · filter=(none) · exclude=(none)
View run · Artifacts: reports

Signed-off-by: Arun Mani J <j.arunmani@proton.me>
Signed-off-by: Arun Mani J <j.arunmani@proton.me>
@arun-mani-j

Copy link
Copy Markdown
Contributor Author

review

@arun-mani-j arun-mani-j merged commit 5c18164 into tetherto:main Jun 17, 2026
12 checks passed
@arun-mani-j arun-mani-j deleted the simpleErrors branch June 23, 2026 09:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test-e2e-smoke Triggers smoke e2e test suite [Currently SDK-only] verified Authorize secrets / label-gate in PR workflows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants