-
Notifications
You must be signed in to change notification settings - Fork 5
Poor network-failure handling: ConnectTimeoutError surfaces as cryptic NoOutputGeneratedError #96
Description
Summary
When the network is down or FriendliAI's API is unreachable, the agent fails with a confusing NoOutputGeneratedError that masks the actual ConnectTimeoutError. The error surface is misleading and the recovery path isn't friendly to users.
What happens
- User sends a message during network outage (or FriendliAI is down)
@friendliai/ai-providerthrowsConnectTimeoutError(10s timeout) via undici- Vercel AI SDK's retry-with-exponential-backoff retries 3x, all fail
- SDK's internal stream finishes with
recordedSteps.length === 0 flush()atstream-text.ts:1077rejects all 4 DelayedPromises withNoOutputGeneratedError— not the original network error
Example trace:
```
APICallError [AI_APICallError]: Cannot connect to API: Connect Timeout Error
(attempted address: api.friendli.ai:443, timeout: 10000ms)
at handleFetchError (@ai-sdk/provider-utils/.../handle-fetch-error.ts:51:14)
at postToApi (@ai-sdk/provider-utils/.../post-to-api.ts:164:11)
at FriendliAIChatLanguageModel.doStream (@friendliai/ai-provider/.../friendli-chat-language-model.ts:447:50)
at wrapStream (packages/cea/src/middleware/trim-leading-newlines.ts:38:33)
at streamStep (ai/.../stream-text.ts:1622:17)
cause: ConnectTimeoutError { code: 'UND_ERR_CONNECT_TIMEOUT' }
NoOutputGeneratedError [AI_NoOutputGeneratedError]: No output generated. Check the stream for errors.
at Object.flush (ai/.../stream-text.ts:1077:17)
```
Why this is bad UX
- User sees "No output generated" — completely unactionable
- The actual root cause ("I can't reach the internet") is buried behind it
- No retry / backoff / user-friendly message at the TUI/headless layer
- Previous unhandled-rejection crash is fixed (#TBD), but the error rendered to the user is still unhelpful
Expected behavior
When a network / connection error occurs:
- Detect `ConnectTimeoutError` / `AI_APICallError` (with `isRetryable: true`) in the stream error path
- Report a clear message to the user: "Cannot connect to `api.friendli.ai` (timeout 10s). Check your network connection."
- Optional: offer to retry with a manual trigger (`r` key) or auto-retry with exponential backoff at the agent-loop level (separate from SDK's internal retries)
- Do NOT surface `NoOutputGeneratedError` to the user when the real cause is a network failure
Where to handle
Candidates:
- `packages/tui/src/agent-tui.ts` — `handleStreamTurnError` / `retryStreamTurnOnNoOutput`: detect if the wrapped error has `cause?.name === 'ConnectTimeoutError'` or `cause?.code === 'UND_ERR_CONNECT_TIMEOUT'`, and render a user-friendly network-error message instead of the generic retry path
- `packages/headless/src/runner.ts` — similar handling for JSONL event emission: emit a specific `network_error` event type
- `packages/harness/src/loop.ts` — decide whether to expose a `networkUnreachable` finish reason
Repro
- Disable network (`sudo ifconfig en0 down` or similar)
- Run `pnpm dev` in `packages/cea`
- Send any message
- Wait 10s → see `ConnectTimeoutError` followed by `NoOutputGeneratedError`
Related
- The unhandled rejection path for `NoOutputGeneratedError` is fixed in the current work tree (patch to `packages/harness/src/agent.ts` silencing stream result DelayedPromises)
- This issue tracks the separate concern of user-facing error messages for network failures
Environment
- OS: macOS (darwin)
- Node: 22.x
- `ai`: 6.0.116
- `@friendliai/ai-provider`: 1.1.7
- `@ai-sdk/provider-utils`: 4.0.15