Errors and failures
Understand the shared Convex error categories and who owns recovery.
Better Convex Nuxt exposes one public call error across client composables and server helpers: ConvexCallError.
Error kinds
| Kind | Meaning | Typical owner of recovery |
|---|---|---|
authentication | Required identity is missing, token exchange failed with auth status, or identity changed during a call | Auth UI or operation caller |
transport | Network, timeout, abort, malformed response, or library-owned HTTP boundary failure | Retry/offline UI or operator |
server | A structured Convex application/function error | Application workflow |
unknown | No stable mechanical classification is available | Fallback handling and diagnostics |
There is no generic validation kind. The module does not infer categories from message text.
Every error also carries functionName, the Convex function path such as projects:update, when the failing call knows it.
Stable codes
Failures that Better Convex raises itself carry a stable code:
| Code | Meaning |
|---|---|
IDENTITY_CHANGED | The signed-in identity changed before or while the call was sent |
CANCELLED | cancel(), reset(), or a disposed scope stopped the work |
FILE_TOO_LARGE | An upload is larger than maxSize |
FILE_TYPE_NOT_ALLOWED | An upload does not match allowedTypes |
UPLOAD_IN_PROGRESS | A second upload started while the first one is pending |
SUBMIT_IN_PROGRESS | A second form submission started while the first one is pending |
UNAUTHENTICATED | A server operation requires a signed-in identity |
CLIENT_UNAVAILABLE | No browser Convex client exists, for example during server rendering |
Application errors keep the code that your Convex function puts in ConvexError data. Check both kinds of code with isConvexCallError(error, code).
Was the request sent?
A browser write can fail before it is sent, or after it was sent but before its result arrived. The library records which one in outcome:
'not-sent': the request never reached Convex. Nothing was written.'unknown': the request was sent, and no result arrived. The write may have happened.undefined: the failure is not about sending, for example Convex rejected the call with aConvexError.
outcome comes from what the library did with the request. It is never guessed from the code. Uploads also carry phase, the upload step that failed. See Error types.
One rejected-Promise contract
mutate and run reject with ConvexCallError:
import { isConvexCallError, normalizeConvexError } from '@lupinum/better-convex-nuxt/errors'
const { mutate: saveProject } = useConvexMutation(api.projects.update)
try {
await saveProject({ projectId, name })
} catch (error) {
if (isConvexCallError(error, 'PROJECT_ARCHIVED')) {
showArchivedNotice()
return
}
reportFailure(normalizeConvexError(error).kind)
}useConvexForm resolves expected validation and mutation failures as { ok: false, error } instead. Callers decide how to project a caught error into local UI state.
Domain meaning belongs to the application
A Convex function can throw structured ConvexError data such as:
throw new ConvexError({
code: 'PROJECT_ARCHIVED',
message: 'Archived projects cannot be edited',
})The module classifies this as server, keeps data unchanged, and copies data.code to code. The message is the text you wrote: a string data, else data.message, else the generic Convex application error. Your application decides that PROJECT_ARCHIVED disables editing or redirects elsewhere.
Write data.message for the person who reads it, because components usually show error.message. Do not put secrets or internal details in it. Use code for decisions. Do not parse the message to discover business meaning.
Convex's own wire message is never used, because it can contain function stack frames. Unclassified strings, objects, and errors use Unknown Convex error. This rule is identical in browser, SSR, and server calls.
Serialized errors
SSR errors cross the Nuxt payload as a public serialized shape. The client revives that shape as ConvexCallError, including functionName.
A Nitro route can return the same shape, without functionName, with toConvexH3Error(error). In the browser, normalizeConvexError() revives it from the FetchError of $fetch or useFetch. Revival accepts only the exact public shape; any other object stays unknown.
ConvexCallError does not retain a raw upstream cause. JSON, structured clone,
SSR HTML, normal inspection, and direct property access therefore cannot recover
an upstream response body, credential, stack, or request object from the public
error. Use the public fields for product behavior. For server logs, add your
own operation name and request ID.
Retry is contextual
- Retry a transient transport failure when the operation is safe to repeat.
- Do not retry authentication failures until auth state changes.
- Let the product workflow decide whether a server error is retryable.
- Do not retry a write with
outcome: 'unknown'blindly; it may have reached the server. Make the write idempotent first.
See error handling for UI patterns and credentials and security for server-boundary controls.