SmartSpectra SDK
LLM Insights

LLM Insights Error Handling & Troubleshooting

The two LLM Insights error surfaces, common failure modes, retry guidance, and how to surface errors in your UI.

LLM Insights can fail in two distinct places, and telling them apart is the key to handling them correctly. This page covers the two error surfaces, the common failure modes and which surface each shows up on, how to retry, and how to surface errors in your UI. For the feature itself — concepts, the request/response model, configuration, and privacy — see the LLM Insights overview.

The two error surfaces

SurfaceWhenMeaningDelivered as
1. Request failed to sendSynchronously, from the request callThe request never left the deviceA non-OK status / thrown error from RequestInsight
2. Response carries an errorAsynchronously, on the response sinkThe request was sent, but the server failedAn Insight with error set instead of analysis

A successful request call does not mean the insight was delivered. The network request is asynchronous: RequestInsight returns as soon as the request is enqueued, not when the server replies. Anything that goes wrong on the wire — connectivity, timeouts, rate limits, server errors — comes back later on Surface 2, not from the request call.

Surface 1 — the request failed to send

This surface is synchronous and local: the request was rejected before it was dispatched, so no response will ever arrive for it. In the C++ SDK the call returns a SmartSpectraError; the exact codes are:

  • kInvalidState — there is no active session. Call Start() first (or the session was already stopped).
  • kProcessingFailed — the request could not be dispatched. Causes: the prompt exceeds the 2048-byte size limit (see Writing prompts); no response sink was registered before requesting; the SDK could not reach the server to establish the session (this is the one case where a network failure surfaces synchronously); or the session is shutting down.

How each platform reports a failed request:

PlatformMechanism
C++RequestInsight returns SmartSpectraError — check err.ok(), read err.FullMessage()
AndroidrequestInsight throws — wrap in runCatching { … }.onFailure { … }
SwiftrequestInsight(_:) throws — use do { try … } catch { … }
Node.jsrequestInsight throws — use try { … } catch { … }

Surface 2 — the response carries an error

This surface is asynchronous: the request was sent and the reply arrived on your response sink, but it carries an error message instead of analysis. The analysis and error fields are a result oneof — exactly one is set — so branch on which is present. The error string typically includes an HTTP status code (for example, 429 or 503), or a transport error.

How each platform reports a response-level error:

PlatformMechanism
C++insight.has_error()insight.error()
Androidinsight.hasError()insight.error
Swiftinsight.result.error(String) (or insight.error)
Node.jsdecode the buffer → insight.error (the result oneof)

A third outcome: no response at all

A request can also produce no response — the sink is simply never called. This is not an error; it is how the service signals "nothing to analyze" (for example, when a session has already produced its analysis and a further request adds nothing). Because there is no callback and no error, your UI must not wait forever — see Retry patterns and best practices.

Response error status codes

When a response carries an error (Surface 2), the message includes an HTTP status. Use it to decide whether to retry:

StatusMeaningRetry?
400Malformed request payloadNo — fix the request
401API key or OAuth setup is missing or invalidNo — fix your credentials
403Key is not authorized for this resourceNo — check account access
404 / 410The insights session expiredRetry
408The server timed out handling the requestYes — back off and retry
429Quota or credits exhausted, or rate limitedBack off; if out of credits, top up
500 / 502 / 503 / 504Internal server errorYes — retry with backoff

4xx statuses mean the request or account needs attention — retrying unchanged won't help, except 404 / 410, which are safe to retry. 5xx statuses are server-side and transient. An out-of-credits 429 is fixed by topping up in the Developer Admin Portal.

Not every failure has an HTTP status: a transport-level failure (connection refused, host unreachable, client-side timeout), or an error reporting that the server returned no analysis, also arrives as an error — treat both as transient and retry with backoff.

Common failure modes

Failure modeSurfaceWhat you observeWhat to do
No active session1kInvalidState / thrownStart() a session before requesting
Prompt exceeds the size limit1kProcessingFailed / thrownShorten the prompt to ≤ 2048 bytes (see Writing prompts)
No response sink registered1kProcessingFailed / thrownRegister the sink before the first request
Can't reach server to establish the session1kProcessingFailed / thrownCheck connectivity and API key; safe to retry
Network unavailable / server unreachable (after dispatch)2error with a transport messageRetry with backoff
Request timed out2error (times out after ~30 s)Retry with backoff
Server returned an error status2error including an HTTP statusLook it up in Response error status codes
Empty / insufficient metrics bufferneitherOn-demand request is sent prompt-only; auto-fired vitals are skippedAllow ~15 s of measurement to warm up the buffer
Nothing to analyzeneitherSink is never calledUse a UI timeout (below); don't block indefinitely

Retry patterns and best practices

The SDK does not retry insight dispatches — there is no automatic retry or backoff. Retrying is your application's responsibility, and the right policy depends on the surface:

  • Surface 1, kInvalidState — do not retry blindly. Ensure a session is running (Start()) and only then request. Retrying against a stopped session will just fail again.
  • Surface 1, kProcessingFailed — usually transient (session or connectivity). Make sure a response sink is registered, then retry once or twice with a short delay.
  • Surface 2, transient errors (network unavailable, timeout, 5xx, or an error reporting no analysis was returned) — retry with exponential backoff.
  • Surface 2, 429 — back off aggressively; if it means credits are exhausted rather than rate limiting, top up instead of retrying.
  • Surface 2, permanent errors (400 bad request, 401/403 auth) — do not retry as-is. Fix the prompt, API key, or account first.

Additional practices:

  • Debounce user-triggered requests. Disable the trigger (button, etc.) while a request is pending, since rapid back-to-back requests are all sent — and repeated requests within a single session may not each produce a reply, so extra requests can waste quota without adding analyses.
  • Always set a UI timeout. A request can produce no response at all (the "nothing to analyze" outcome above). A failed request resolves within ~30 seconds via Surface 2, but a "nothing to analyze" result never calls back — so add your own pending-state timeout and clear the spinner.
  • Correlate with the request ID. Match the request_id on each Insight against the ID returned by your request so a late or failed reply updates the right pending item (and so you can ignore auto-fired vitals, which won't match a request you made).

Surfacing errors in your UI

Two reference C++ samples show both surfaces end to end. Use them as templates:

  • smartspectra/cpp/samples/insights_example/main.cc — a minimal OpenCV app. It renders something for every outcome: [request failed] <message> when the request call fails (Surface 1), [error] <message> when a response carries an error (Surface 2), [empty response] when a reply has neither field set, and a [waiting for response…] placeholder while a request is in flight. Handling all outcomes — including the empty one — is the pattern to copy.
  • smartspectra/cpp/samples/winui3_example/ (see SmartSpectraWinUI/MainWindow.xaml.cpp) — a production-shaped WinUI 3 app. It maps typed error codes to friendly strings (for example, authentication → "Authentication failed. Check your API key.", credits → "Account credits exhausted.", network → "Network issue — please try again."), disables the "Ask AI" button and shows "Analyzing…" while a request is pending, and re-enables it when a response or a synchronous failure arrives.

Distilling both into a checklist:

  • Map error codes/messages to friendly strings for the user; keep the raw message in your logs.
  • Show a pending state and disable the trigger while a request is in flight.
  • Re-enable and clear the pending state on every terminal outcome — success, error, and an empty/absent response. If you only clear it on success and error, a "nothing to analyze" reply leaves your control stuck.
  • Add a timeout for the no-response case so the UI recovers on its own.
  • Marshal UI updates onto the UI thread. In C++ the insight callback runs on a background thread; Swift delivers on the main actor and Android on the main thread, but if you fan out to your own handlers, keep UI writes on the UI thread.

Common symptoms and fixes

RequestInsight returns kInvalidState (or the call throws immediately)

The session isn't running. Call Start() and wait for the session to be active before requesting.


RequestInsight returns kProcessingFailed (or the call throws immediately)

Either no response sink was registered, or the SDK couldn't reach the server to establish the session. Register your insight sink before the first request, verify network connectivity and that your API key is valid, then retry.


A response's error includes an HTTP status

Look the status up in Response error status codes. In short: 4xx means the request or account needs fixing (retrying unchanged won't help, except 404 / 410), while 5xx, transport failures, and errors reporting the server returned no analysis are transient — retry with backoff.


No insight ever arrives

Three benign causes, in order of likelihood: (1) the metrics buffer hasn't warmed up yet — wait ~15 seconds after Start(); (2) no response sink was registered, so replies have nowhere to go — register it before starting; (3) the server had nothing to analyze and returned no result, so the sink was deliberately not called. Because (3) is silent, always back a pending request with a UI timeout.

Important: LLM Insights are generated by a large language model and can be inaccurate, incomplete, or misleading — language models sometimes produce confident but false statements ("hallucinations"). Treat every insight as general wellness information, not medical advice, and confirm anything important before acting on it. The SDK metrics themselves are offered for general wellness and informational purposes only; they have not been cleared by the FDA and may not be used for medical diagnosis or treatment.

Getting Help

On this page