Skip to content

Poor network-failure handling: ConnectTimeoutError surfaces as cryptic NoOutputGeneratedError #96

@minpeter

Description

@minpeter

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

  1. User sends a message during network outage (or FriendliAI is down)
  2. @friendliai/ai-provider throws ConnectTimeoutError (10s timeout) via undici
  3. Vercel AI SDK's retry-with-exponential-backoff retries 3x, all fail
  4. SDK's internal stream finishes with recordedSteps.length === 0
  5. flush() at stream-text.ts:1077 rejects all 4 DelayedPromises with NoOutputGeneratedErrornot 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:

  1. Detect `ConnectTimeoutError` / `AI_APICallError` (with `isRetryable: true`) in the stream error path
  2. Report a clear message to the user: "Cannot connect to `api.friendli.ai` (timeout 10s). Check your network connection."
  3. 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)
  4. 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

  1. Disable network (`sudo ifconfig en0 down` or similar)
  2. Run `pnpm dev` in `packages/cea`
  3. Send any message
  4. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions