---
title: Errors
description: The one error body every call returns, what its fields mean, and which codes to retry.
type: reference
product: platform
summary: One problem body, a stable code, and the codes worth retrying.
updated: 2026-09-28
order: 6
---

A failed call on the one API's contract routes answers with one JSON body, sent
as `application/problem+json`. The shape follows RFC 9457, the Problem Details
standard, with the fields Sylphx adds to it. A few older routes, such as
`whoami` and the organization routes, still answer a plain JSON error body with
an `error` code; read the HTTP status first and the `code` or `error` field
second.

## The error body

<PropertyTable
	properties={[
		{
			name: 'type',
			type: 'string',
			description: 'A link that identifies the kind of problem.',
		},
		{
			name: 'title',
			type: 'string',
			description: 'A short human summary of the problem.',
		},
		{
			name: 'status',
			type: 'number',
			description: 'The HTTP status of the response.',
		},
		{
			name: 'detail',
			type: 'string',
			description: 'What went wrong in this instance.',
		},
		{
			name: 'instance',
			type: 'string',
			description:
				'The request id, the same value as the `Sylphx-Request-Id` response header.',
		},
		{
			name: 'code',
			type: 'string',
			description: 'The stable machine code, written in upper snake case.',
		},
		{
			name: 'grpc_status',
			type: 'string',
			description: 'The same failure as a gRPC status.',
		},
		{
			name: 'retryable',
			type: 'boolean',
			description: 'Whether retrying the same call can succeed.',
		},
		{
			name: 'effect',
			type: 'string',
			description:
				"Whether the call had any effect: `none`, `applied`, or `unknown` when the platform cannot tell.",
		},
		{
			name: 'retry_after_ms',
			type: 'string',
			description:
				'How long to wait before retrying, as a decimal number of milliseconds carried in a string. Present only when the wait is known.',
		},
		{
			name: 'details',
			type: 'array',
			description:
				'Field-level detail, as an array of objects. Present only when the problem is about specific fields.',
		},
	]}
/>

<Callout tone="note" title="Branch on the code">
	Branch on `code`, never on `status`. The code is stable and names the
	problem exactly; the status is a summary of it.
</Callout>

## The codes

This is the complete set.

- `UNKNOWN_FIELD`
- `INVALID_FIELD`
- `PAGE_TOKEN_MISMATCH`
- `IDEMPOTENCY_KEY_REUSED`, HTTP 422
- `IDEMPOTENCY_IN_PROGRESS`
- `ETAG_MISMATCH`
- `RESOURCE_NOT_FOUND`
- `RESOURCE_ALREADY_EXISTS`
- `UNAUTHENTICATED`
- `PERMISSION_DENIED`
- `DELETION_PROTECTED`
- `RECONCILE_FAILED`
- `PLAN_LIMIT_REACHED`, HTTP 402
- `SPEND_LIMIT_REACHED`, HTTP 402
- `RATE_LIMITED`, HTTP 429
- `INVALID_STATE`
- `INTERNAL`, HTTP 500
- `UNAVAILABLE`, HTTP 503
- `UNIMPLEMENTED`, HTTP 501
- `NOT_YET_PROPAGATED`, HTTP 401
- `DEADLINE_EXCEEDED`, HTTP 504
- `NOT_FOUND`, HTTP 404
- `CONTRACT_SKEW`, HTTP 503
- `NO_CAPACITY`, HTTP 503
- `SHAPE_NOT_OFFERED`, HTTP 422
- `STALE_GENERATION`, HTTP 409
- `RESOURCE_IN_USE`, HTTP 409
- `CONTROL_HELD`, HTTP 409
- `CONTROL_HELD_BY_HUMAN`, HTTP 409

## Codes that change what you do

### RATE_LIMITED

You sent more requests in a second than your plan allows. The call is retryable,
and `retry_after_ms` says how long to wait. The rate limits of each plan are on
[Plans, usage and limits](/docs/platform/billing-and-limits).

### SPEND_LIMIT_REACHED and PLAN_LIMIT_REACHED

Both answer HTTP 402. `PLAN_LIMIT_REACHED` means the call would pass an
allowance of your plan; `SPEND_LIMIT_REACHED` means it would pass the monthly
spend cap of your organization. Neither is a fault: the call was refused before
it was billed. Make room under the plan or under the cap, then call again.

### NOT_YET_PROPAGATED

A brand-new key can take a moment to work. The call is retryable: retry it, and
it succeeds once the key is in place.

### ETAG_MISMATCH and STALE_GENERATION

Both mean the state you aimed at has moved. `ETAG_MISMATCH` means the `etag` you
sent is not the resource's current one; `STALE_GENERATION` means the lease
generation or control epoch you named is no longer the current one. Read the
resource again, take the new `meta`, and reapply the change.

### IDEMPOTENCY_IN_PROGRESS

An earlier call carrying the same idempotency key has not finished yet. It is
retryable: wait, then send the same call again rather than a new one.

### UNAVAILABLE

The service is temporarily unavailable. The call is retryable: back off and try
again.

## Retrying

These codes are retryable: `IDEMPOTENCY_IN_PROGRESS`, `ETAG_MISMATCH`,
`RATE_LIMITED`, `UNAVAILABLE`, `NOT_YET_PROPAGATED`, `DEADLINE_EXCEEDED` and
`CONTRACT_SKEW`.

When `retryable` is true, retry with exponential backoff. When `retry_after_ms`
is present, wait at least the number of milliseconds it names before the next
attempt.

## In the client library

The TypeScript SDK throws `SylphxError`, which carries `code`, `status`,
`retryable`, `effect`, `detail`, `requestId` and the raw `problem` body. It
retries 429, 502, 503 and 504 on its own, honouring `Retry-After`.

<CodeTabs>
	<CodeTab
		label="cURL"
		language="bash"
		code={`curl -sS https://api.sylphx.com/v1/whoami \\
  -H "Authorization: Bearer $SYLPHX_API_KEY"

# HTTP 429
{
  "title": "Too many requests",
  "status": 429,
  "detail": "Rate limit exceeded for this key.",
  "instance": "9f2c1a7d4b6e0f31",
  "code": "RATE_LIMITED",
  "retryable": true,
  "retry_after_ms": "1000"
}`}
	/>
	<CodeTab
		label="TypeScript"
		language="ts"
		code={`try {
  await callTheApi()
} catch (error) {
  const problem = error as SylphxError
  if (problem.retryable) {
    console.warn('retrying', problem.requestId)
    return
  }
  console.error('failed', problem.code, problem.requestId)
  throw problem
}`}
	/>
</CodeTabs>

When you ask for support, quote the request id. It is the `instance` in the
body, and the same value the response carried in `Sylphx-Request-Id`.

<RelatedDocs
	links={[
		{ href: '/docs/platform/keys-and-scopes', label: 'Keys and scopes' },
		{ href: '/docs/platform/billing-and-limits', label: 'Plans, usage and limits' },
		{ href: '/docs/platform/versioning', label: 'API versions' },
	]}
/>
