Skip to content

fix: surface a clear error for nova-3 + keywords on streaming#505

Open
valeriudev wants to merge 1 commit into
deepgram:mainfrom
valeriudev:vg/fix-nova3-keywords-474
Open

fix: surface a clear error for nova-3 + keywords on streaming#505
valeriudev wants to merge 1 commit into
deepgram:mainfrom
valeriudev:vg/fix-nova3-keywords-474

Conversation

@valeriudev

Copy link
Copy Markdown

Summary

Opening a streaming listen.v1 connection with a nova-3 model and the legacy keywords parameter fails silently today — the socket closes cleanly (code 1000) with no error, no message, and no close reason, so the caller gets zero signal that anything is wrong.

REST already rejects this exact combination with a clear 400:

INVALID_QUERY_PARAMETER: Keywords are not supported for Nova-3. Please use `keyterm` instead.

This makes streaming behave the same way: a pre-connect guard in WrappedListenV1Client.connect throws the same BadRequestError before dialing, so you get an actionable error on both transports — and it works in the browser, where a failed-handshake body can't be read at all.

Fixes #474. Fixes #470. Supersedes #480 — thanks @peteroyce for the original diagnosis; this adds test coverage and throws a real error instead of only console.warn.

Before / after

Live, against the real API:

case before after
nova-3 + keywords open=false, close=1000, err=none (silent) BadRequestError 400 — "Keywords are not supported for Nova-3. Please use keyterm instead."
nova-3 + keyterm opens ✅ opens ✅
nova-2 + keywords opens ✅ opens ✅

The thrown body mirrors the REST 400 body exactly (err_code + err_msg), so streaming and REST now surface the identical error class — catch (e) { if (e instanceof BadRequestError) ... } works the same on both.

Why a client-side guard, and not "propagate the handshake error"

I instrumented the unfixed streaming path and logged every event. On nova-3 + keywords the SDK emits no open, no message, no error — only close(1000, reason=""). There's nothing to propagate: REST returns a rich 400, but the streaming endpoint closes cleanly with zero detail. A pre-flight guard is the only mechanism that yields an actionable error, and the only one that also works in the browser.

Correctness

Verified against the live API across the whole nova-3 family so the startsWith("nova-3") check matches exactly what the server rejects — no over-blocking:

model keywords
nova-3, nova-3-general, nova-3-medical 400 — rejected
nova-2, nova-2-medical 200 — accepted

Scope

listen.v1 streaming only. listen.v2 (flux) has no keywords parameter, and REST already errors clearly — neither is touched.

Tests

  • New offline regression test tests/unit/keywords-nova3-guard.test.ts (9 cases): the public-client throws-matrix for nova-3 / -general / -medical, plus a pure-predicate matrix for the allowed/empty cases (so no socket is opened for the cases that should succeed).
  • Full suite green: unit 552, wire 132.

Notes

  • src/CustomClient.ts is hand-maintained (in .fernignore), so this survives Fern regeneration; the new test file is added to .fernignore for the same reason.
  • There's a broader observability gap here — any rejected handshake currently surfaces as a silent close(1000). That's out of scope for this fix, but I'm happy to follow up on it separately.

Opening a streaming listen.v1 connection with a nova-3 model and the
legacy `keywords` parameter used to fail silently: the socket closed
cleanly (code 1000) with no error, message, or close reason, so callers
got no signal that anything was wrong.

REST already rejects this combination with a 400 ("Keywords are not
supported for Nova-3. Please use `keyterm` instead."). This adds a
pre-connect guard that throws the same BadRequestError before dialing,
so streaming and REST behave identically — and it works in both Node and
the browser, where a failed handshake body can't be read at all.

Scope is listen.v1 streaming only: listen.v2 (flux) has no `keywords`
param, and REST already errors clearly.

Fixes deepgram#474
Fixes deepgram#470
@valeriudev valeriudev requested a review from GregHolmes as a code owner June 4, 2026 14:27
@valeriudev

valeriudev commented Jun 5, 2026

Copy link
Copy Markdown
Author

Heads-up for triage: this overlaps #480 (@peteroyce), which also fixes #474. The difference is #480 logs a console.warn then still connects (so the socket dies silently), whereas this throws the same BadRequestError the REST path already does — catchable via instanceof on both transports — plus a regression test. Credit to @peteroyce for the original diagnosis; happy to close in favour of #480 if the lighter-touch warning is preferred.

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.

nova-3 returns HTTP 400 when keywords param is used in streaming WebSocket WebSocket connection fails with keywords parameter (Nova-3, 1006)

1 participant