typeship APIAPI

Errors

One actionable error envelope, stable codes, retry guidance, and a request id.

Every error from the typeship API uses the same envelope:

{
  "errors": [
    {
      "type": "request_error",
      "code": "invalid_request",
      "message": "name is required.",
      "retryable": false,
      "suggested_action": "Fix the request using the operation schema, then retry.",
      "docs_url": "https://typeship.dev/docs/typeship-api/errors"
    }
  ],
  "request_id": "req_k3j2h8f0a1b2c3d4"
}

errors is always an array, even for a single error. request_id also arrives on every error response as the Request-Id header. Quote it when reporting a problem.

Branch on type, code, and retryable. message is for a person and may be reworded. suggested_action is a stable recovery instruction. Validation errors can also carry field as a JSON Pointer.

Codes

CodeStatusRetryWhen
invalid_request400NoMalformed JSON, a failed field validation, a bad cursor, or an operation that does not apply. The message names the field.
idempotency_key_reused409NoThe key already identifies a different project-creation request. Reuse the original parameters or send a new key.
unauthorized401NoMissing, malformed, expired, or revoked bearer credential. Fix the credential first.
organization_required403NoThe OAuth grant has no organization context. Reconnect and choose an organization during consent.
insufficient_scope403NoThe OAuth token lacks the operation's typeship:read, typeship:generate, or typeship:write capability. Reconnect and approve the scope named by WWW-Authenticate, or use an organization API key.
forbidden403NoThe OAuth token has the capability, but the signed-in organization member is not an admin for an admin-only operation. Ask an organization admin; requesting another scope will not help.
plan_limit_reached402NoThe Project or hosted feature is outside the account's plan. Free has no Generation-count quota or Target-count restriction; each Target is capped to 25 operations. The response names where to lift the limit.
not_found404NoThe resource does not exist or belongs to another account. The two are indistinguishable on purpose.
payload_too_large413NoA spec over 10MB, inline or fetched.
spec_error422NoThe document cannot be used as a spec at all. The message is the generator's, quoted verbatim. Warnings never cause this. See Errors and warnings.
fetch_error400 or 502502 onlyThe spec URL was invalid (400) or could not be fetched (502): unreachable, timed out after 15 seconds, or answered with an error status.
rate_limited429After Retry-AfterToo many requests. A Retry-After header says how long to wait. See Rate limits.
internal_error500Once, with backoffThe generator hit an unexpected condition. Not a problem with your spec.

Agent guidance

Read errors[0].code, never the message. Honor retryable; when it is false, change the request or ask the user instead of looping. Three cases deserve care:

  • plan_limit_reached and generation caps are not failures to retry. A capped generation succeeds with a limits object. Report omissions only when omitted_operations is above zero. For reason: "anonymous", offer signup_url. For reason: "free_plan", use upgrade_url instead of asking for another key. A 402 means a stored Project or hosted feature is outside the plan.
  • rate_limited says how long. Wait the Retry-After seconds once. Anonymous calls also carry RateLimit-Limit and RateLimit-Remaining on every response, so pace before the wall, not after.
  • Large results are paged and indexed. A generation over the size cap returns files_omitted: true with a files_index; fetch the files you need with GET /v1/generations/{id}/file?path=... instead of asking for the whole generation again. Lists return has_more and next_cursor.

The typeship CLI turns all of this into its own envelope with stable codes and next_steps (below); the typeship MCP server returns the same bodies as tool errors.

In the typeship SDK

Nothing throws. Every call returns a discriminated result, and the error side is a typed union of the documented errors for that operation. The envelope is the typed body:

const result = await client.projects.create({
  name: "Acme API",
  definition: { source: { kind: "url", url: "https://api.acme.example.com/openapi.json" } },
  targets: [{ name: "Acme TypeScript SDK", generator: "typescript-sdk" }],
});
if (!result.ok) {
  result.error.status;          // 400
  result.error.body.errors[0];  // typed code, retryability, message, and docs URL
  result.response?.requestId;   // "req_..."
}

PaymentRequiredError, NotFoundError, UnprocessableEntityError, and the other documented errors are exported by name. unwrap(result) throws them instead.

See Results and errors for the pattern used by every generated SDK.

In the typeship CLI

Errors print one JSON envelope on stderr and exit 1. The envelope contains status, issues, docs_url, next_steps, and detail.

The API response is available under detail.body. CLI issue codes derive from status and error type, including NO_AUTH, PLAN_LIMIT, RATE_LIMITED, and SPEC_INVALID.

Usage mistakes exit 2 before making a request. See Output and exit codes.

On this page