Errors
Last updated
When Terminus refuses a request, it answers a problem: a small JSON object, the same shape everywhere, whose code says what went wrong. Branch on the code and the status, never on the words.
On this page⌄
What an error looks like#
Every answer that is not a success is application/problem+json, the IETF's standard error format (RFC 9457), with Terminus's code beside its own fields:
{
"type": "https://terminus.build/docs/errors#version_conflict",
"title": "Version conflict",
"status": 409,
"detail": "expected version 3, the record is at 4",
"code": "version_conflict"
}- type
- Where the code is explained: its row on this page.
- title
- A short summary of the code, the same every time it is answered.
- status
- The HTTP status, which a code always has.
- detail
- What happened this time, in words for people.
- code
- What went wrong, for a program to branch on.
- details
- Facts to act on, for the codes that carry any: the price to confirm, the service to connect, the version the data is at.
The SDK reads it for you: every refusal is a TerminusError whose code, title and details are the problem's, and whose message is its detail.
Terminus's OpenAI-compatible chat completions answer OpenAI's own error object instead, {"error": {"message", "type", "param", "code"}}, because OpenAI's clients read it. Its code is one of these.
Codes#
Every code, with its HTTP status and its title:
- bad_request
- 400 · Bad request. The request is malformed: a path, query, header or body that is not valid, a field Terminus does not know, or a value out of range.
- invalid_cursor
- 400 · Invalid cursor. The
cursoris not one this list gave out for this query: it belongs to another list or another query, or it was changed. Ask for the first page again. - unauthorized
- 401 · Authentication required. No valid credential: the one the door takes (a session, a token, a client's secret or a signature) is missing or unknown, or someone who is not signed in asked for something that needs an account.
detailsays what the door takes, and how it is sent. A sign-in with the wrong email or password answers it too. - session_ended
- 401 · Session ended. The session was signed out or has expired: sign in again. An app stops and says so, and opening it again signs in.
- forbidden
- 403 · Forbidden. Signed in, but not allowed: a role, a block, or what an app declared does not permit it.
- grant_required
- 403 · Consent required. The person has not yet agreed to share this with the app. Ask for their consent rather than showing a plain refusal.
- not_connected
- 403 · Service not connected. The service works in each person's own account, and this person has not connected it, or must connect it again.
detailsnames the service (service_id,address,name), how it connects (connect:sign_inorkey) and why (reason:not_connectedorreconnect). Nothing was sent. - collection_undeclared
- 403 · Collection not declared. The app's release does not declare this collection. An app declares every collection it uses in its schema.
- not_found
- 404 · Not found. Nothing by that name or address is visible to the caller.
- not_installed
- 404 · App not installed. The person has no installation of this app, so there is nothing to change.
- method_not_allowed
- 405 · Method not allowed. The address exists, but not with this method:
Allowlists the ones it takes. - conflict
- 409 · Conflict. The request conflicts with how things stand: a name already taken, a relation it would break, or an app updated since the session began.
- version_conflict
- 409 · Version conflict. An expected version or hash no longer matches: read again, then retry with the new one.
- idempotency_conflict
- 409 · Idempotency key reused. The
Idempotency-Keywas already used for a different request. - still_pending
- 409 · Still pending. A request with the same
Idempotency-Keyhas not finished. Retry later with the same key. - lease_lost
- 409 · Lease lost. The job is no longer this worker's: its lease ran out and another worker took it over, or the job ended. Stop working on it: what this worker writes for it now is refused.
- outcome_unknown
- 409 · Outcome unknown. The work was cut off after it had begun to send something out of Terminus, such as an email, a call to a service or an action on a desktop, so that may or may not have happened. It is not sent again: find out what happened before you try again.
- update_required
- 409 · Update required. This release of the app is behind the data it writes, so it may read but not write until the app updates.
details.data_versionis the data's version, anddetails.kindispersonorspace. - data_migration_failed
- 409 · Data migration failed. The app's data did not pass one of its migrations:
details.versionnames the migration, anddetails.messagesays what went wrong. Installing or updating the app is refused, and the installation stays where it was. Writing records is refused, and nothing of the write is stored, when the records of a write queued under an earlier release do not pass the migrations that release lacked, or when the person's or space's data it writes into has not passed a migration of the app's release; that data can still be read, and takes writes again once it passes. - client_update_required
- 426 · Client update required. This version of the Terminus CLI or desktop is older than the oldest Terminus accepts.
detailsays what to install, anddetailsnames the client (kind:cliordesktop), itsversion, theminimumand, for the desktop, theurlit is downloaded from. Nothing was done. - payload_too_large
- 413 · Payload too large. A body, a field, or something fetched is larger than its limit.
- unprocessable
- 422 · Unprocessable content. The request is well formed, but its content breaks a rule its data declares: fix the data, not the request.
- refused
- 422 · Refused. An app's server or a service refused the request.
details.codeanddetails.statusare what it answered, and the detail is its own words. - room_code_failed
- 422 · Room code failed. The room's code failed the step this message was handed to: it threw, answered what Terminus cannot keep or send, or ran past its time. Nothing of that step was kept or sent.
- rate_limited
- 429 · Rate limited. Too many requests, or what the request changes is busy with another change. Wait as long as
Retry-Aftersays, when it says, then try again. - quota_exceeded
- 402 · Quota exceeded. The caller's credits, or storage, are used up for this.
- price_confirmation_required
- 402 · Price confirmation required. A purchase that did not confirm its price, or confirmed another one. Nothing was charged:
details.price_creditsis the price due, so ask the person, then repeat the request with it. - url_not_allowed
- 400 · URL not allowed. Terminus will not reach this address: it is not a public
httporhttpsURL on a standard port. - upstream_failed
- 502 · Upstream failed. Something Terminus called for the request failed or answered badly: a website, a model's provider, a service, an app's server.
- 503 · App server unavailable. The app's server cannot run right now. Try again shortly.
- 503 · Service unavailable. Terminus is busy, or something it needs is not available. Try again shortly.
- space_migrating
- 503 · Space migrating. A space's data is moving to a new version of the app. Try again after
Retry-After. - deadline_exceeded
- 504 · Deadline exceeded. The request, or something it waited on, did not finish in time.
- offline
- 503 · Offline. The device has no connection to Terminus, and this needs one. Nothing was sent: try again once it is back online.
- not_read
- 503 · Not read. A read of a copy's pass that its request did not make: the request's budget was spent before it, or while it was being made. Nothing was read; the copy asks for it again in its next request.
- not_implemented
- 501 · Not implemented. This host does not do this.
- unsupported_in_dev
- 501 · Not available in terminus dev.
terminus devdoes not do this on a developer's machine; the published app does. - internal_error
- 500 · Internal error. Terminus failed, through no fault of the request. Trying again may work.
- invalid_path
- 400 · Invalid path. The file path is not one Terminus can act on.
- case_conflict
- 409 · Case conflict. A new name differs from one already there only in letter case: a Mac cannot keep both, so the second is refused.
- destination_exists
- 409 · Destination exists. A move's destination already holds a file or a folder.
- not_empty
- 409 · Folder not empty. A folder that may only be deleted while it is empty still holds a file.
- file_too_large
- 413 · File too large. The file is too large to be read whole here: download it instead.
- range_not_satisfiable
- 416 · Range not satisfiable. The
Rangeasks for bytes the file does not have:Content-Rangegives its size. - window_command
- 409 · Window command. This command runs in the app's window on the Terminus desktop, never through the API.
For your coding agent
https://www.terminus.build/