novo agents

Troubleshooting

Symptom-first fixes for the failures integrators actually hit. The full error catalog with every code lives in Errors.

The stream ended but the run seems unfinished

Cause: you treated stream EOF as completion. A run is finished only when the stream yields the data-novo-terminal chunk; a connection can drop or hit a platform timeout while the run keeps executing on Novo's side.

Fix: use streamToCompletion() (it reconnects until the terminal chunk arrives), or check stream.reachedTerminal and resume with stream.reconnect({ lastEventId }). On Vercel, also set export const maxDuration on any route that proxies the stream, because the platform default will cut long streams mid-run. See Reconnect a dropped stream.

NovoConfigurationError: browser_runtime

Cause: the SDK got into a browser bundle. It is server-only by design; the API key must never reach a client.

Fix: move the NovoAgents construction behind a server route and proxy the stream with stream.toResponse(). See Stream into a Next.js route.

401 on every request

Cause: missing or wrong key.

Fix: confirm the env var is exactly NOVO_AGENTS_API_KEY, the value starts with novo_ag_, and the process actually has the env loaded (a common miss in test runners and workers). Keys are shown once at creation; if it's lost, rotate it in the console under api keys.

409 thread_busy when sending a message

Cause: the thread already has a running turn. This is not an error to retry blindly: your message was accepted as a queued follow-up and will be delivered at the next turn boundary.

Fix: the 409 body includes a followup_id; withdraw it if the message is stale, or just let it deliver. Don't spin up a second thread to work around it: one conversation, one thread.

The run is stuck waiting_for_approval / waiting_for_input

Cause: the agent paused for a human (a tool approval or an ask_question) and nothing resolved it.

Fix: resolve the pending interaction with novo.interactions.resolve(...). One trap worth knowing: an approval is resolved against the interaction's own interaction.id, but a question is resolved against the inner details.questions[].id or details.actions[].id; using the top-level id for a question returns 400. Unresolved pauses eventually terminate as interaction_timeout. See Human in the loop.

Runs are rejected with strict_data_handling_unavailable

Cause: the run required dataHandling: 'strict' but some route it needs (band or a managed capability) has no verified zero-data-retention path. Strict never silently falls back; it rejects before inference.

Fix: check novo.bands.list() for per-band strict availability, and trim the run to strict-eligible capabilities. See Capabilities.

quota_exceeded or rate_limited

Cause: the workspace hit its monthly spend cap (quota_exceeded) or its run-start/active-run limits (rate_limited). These raise automatically with billing history.

Fix: ask an organization admin to open Billing in the Novo console, review the current level and caps, and purchase credits if a higher level is needed. Per-run overspend is a different control: pass budgetCents to stop an individual run (terminalReason: 'budget_exceeded').

The numbers don't add up

Cause: usually a partition misunderstanding, not a billing bug. A run's cost is the sum of its run-scoped usage rows: the root run's inference plus one subagent_run row per delegated worker plus per-generation managed_media rows. Browser, workspace, and document-processor compute are thread-scoped managed_runtime rows that can settle outside a live run.

Fix: reconcile with usage.events.list({ runId }) (and { threadId } for runtime rows); row sums equal the billed cost exactly. A $0 subagent_run row is a credited engine-fault attempt: platform failures are never billed. See Usage.

First run on a thread is slow

Cause: cold start; the durable runtime scales to zero.

Fix: call threads.prewarm(threadId) 10–30 seconds before the first message (it's a free no-op when already warm). See Pre-warming.

Still stuck

Every error response carries an X-Request-Id, and engine faults carry a structured support reference; quote either in a ticket. The error envelope and SDK error subclasses are documented in Errors.