{success:false} JSON envelope, throws a typed error instead of
resolving. All three error classes are exported from the package root:
Error class hierarchy
PersonaApiError
Base error for any non-2xx or{success:false} response from the API. Mirrors the backend’s error
envelope: {success:false, status, statusCode, message, code, timestamp}.
message— the human-readable string from the envelope (inherited fromError). Safe to log.code— mirrors the backend’s machine-readable code (e.g.VALIDATION_ERROR,UNAUTHORIZED,PROJECT_NOT_ACTIVE,NOT_FOUND,EXTERNAL_USER_REQUIRED). Falls back toUNKNOWN_ERRORwhen the response body carries no code.response— the raw parsed error body ({success:false, message, code, ...}), useful for debugging. May beundefinedfor non-JSON error responses.
PersonaAuthError
Thrown on401 or 403 — invalid/missing credential, or the Project isn’t ACTIVE.
A subclass of PersonaApiError (so instanceof PersonaApiError also matches).
PersonaValidationError
Thrown on400 — request validation failed (bad/missing fields, invalid enum values, etc.).
A subclass of PersonaApiError.
Handling errors
PersonaValidationError before
PersonaApiError, since the subclasses pass instanceof PersonaApiError).
What causes each error
Common failure scenarios
Authentication / authorization failures
- Wrong or revoked credential → 401
PersonaAuthError. VerifyPERSONA_CREDENTIALand re-mint via Studio if needed. - Project suspended / not active → 403
PersonaAuthErrorwithPROJECT_NOT_ACTIVE. - Cross-identity access: a runtime-plane client (with
externalUserId) calling control-plane resources like Providers → 404 (existence-hiding) or empty lists, not a loud auth error. Providers can never be owned by an external user.
EXTERNAL_USER_REQUIRED (400)
Threads, Files, Memory, and Chat requireexternalUserId on the client — a bare Project
credential has no Subject to scope to, and the server rejects the call with 400 EXTERNAL_USER_REQUIRED (PersonaValidationError). Stores file CRUD throws the same when the
target Store’s scope is 'externalUser'.
Fix: construct a runtime-plane client (externalUserId: currentUser.id) and retry.
Validation failures (400)
- Missing required fields (
name,systemPrompt,providerIdon Agent create, etc.). contextOverridelonger than 4000 chars → 400INVALID_CONTEXT_OVERRIDE(rejected, not truncated).bulkDeletewith more than 100 ids → 400 before anything is deleted.- Uploading more than 10 files or a file over 20MB to a Knowledge base → 400.
- Unsupported upload file types (not PDF/TXT/MD/JSON/CSV) → 400.
scope: 'mine'on a control-plane client (noexternalUserId) → the server rejects it.
Not found / ownership (404)
get()/update()/delete()on an id the credential’s scope doesn’t own → 404PersonaApiError. The API deliberately does not distinguish “doesn’t exist” from “not authorized” (existence-hiding).chat.stream()with athreadIdthat doesn’t belong to the asserted user + agent → 404. It does not silently fall back to a different conversation.
Network / API failures
- DNS/TCP failures, timeouts, and aborted requests throw the runtime’s own network errors (e.g.
TypeError: fetch failed) — notPersonaApiError. Wrap accordingly, and don’t typecheck only against the Persona classes. - 429 rate limits are retried automatically; only after
maxRetriesis exhausted does aPersonaApiErrorwithstatusCode: 429surface.
Errors that are NOT thrown
bulkDelete()partial failures do not throw — partial failures land in the result’sfailedarray. Check it.testConnection()on Providers/MCPs returns{ success: false, message }for a reachable endpoint that rejects the credentials — that’s a result, not a thrown error. A genuinely unreachable endpoint throws.- Structured chat run failures (provider auth, tool timeout, context length exceeded) are
not thrown by
chat.sendMessage()— they’re returned asresult.error(aPersonaRunErrorEvent). See Chat.