Errors and request IDs
One correlation ID on every response, plus an actionable error envelope, stable types and codes, and retry guidance.
Every error uses one envelope, and errors is always an array, so one code path handles every failure:
{
"errors": [
{
"type": "request",
"code": "input_missing",
"field": "/name",
"message": "name is required.",
"retryable": false,
"suggested_action": "Supply the required value at field, then retry.",
"docs_url": "https://typeship.dev/docs/typeship-api/errors#input_missing"
}
],
"request_id": "req_k3j2h8f0a1b2c3d4"
}| Field | Description |
|---|---|
type | Who can resolve the error. See Error types. |
code | Stable identifier for the specific failure. Each code belongs to one type and links to its row below. |
message | Explanation for a person. Its wording may change; do not parse it. |
retryable | true when another attempt can succeed without changing the request. |
suggested_action | Stable, concise recovery instruction for a person or agent. |
docs_url | Link to this code's row on this page. |
field | JSON Pointer to the input that caused the error, when one did. |
in | Request part containing field: body, query, or header. Omitted means body. |
phase | On recorded failures, the stage that failed. See Errors on resources. |
target_id | The affected Target when one response reports failures for several Targets. |
Handling errors
Branch on type and code, and honor retryable. Use message only for logs and display.
const body = await response.json();
for (const error of body.errors ?? []) {
if (error.type === "rate_limit" || error.code === "idempotency_key_in_use") {
await sleep(Number(response.headers.get("Retry-After") ?? 1) * 1000);
return retry(); // same request, same Idempotency-Key
}
if (error.type === "auth") return reauthenticate(error.code); // auth_required, api_key_invalid, token_invalid
if (error.type === "organization") return askToUpgrade(error.message); // quota_exceeded, feature_not_available
if (error.retryable) return retryOnceWithBackoff();
return fix(error.field, error.suggested_action); // change the request before retrying
}- Read every entry. A
400lists up to 50 independent problems so you can fix them in one pass.fieldis a JSON Pointer into the part named byin; header pointers use lowercase names, such as/idempotency-key. - Do not retry plan limits. A capped generation still succeeds and reports omitted operations in
coverage. Forcoverage.reason: "anonymous", offersignup_url; for"free_plan", offerupgrade_url. - Wait the stated time.
Retry-Aftergives the seconds to wait on a429. Responses carryRateLimit-LimitandRateLimit-Remaining, so you can pace before the limit.
The Typeship SDKs raise typed errors carrying this envelope, the Typeship CLI wraps it in its own JSON envelope, and the Typeship MCP server returns it as the tool error.
Error types
| Type | Meaning | What to do |
|---|---|---|
request | The request cannot be applied as sent: invalid input, a missing resource, or a state conflict. | Change the request, or follow the code's recovery for a state conflict. |
auth | The credential is missing, invalid, or lacks the grant the operation needs. | Fix the credential or its grant. Retrying will not help. |
idempotency | The Idempotency-Key is invalid, reused for different input, or still in use. | Send a valid or new key, or wait and retry with the same key. |
rate_limit | Too many requests. | Wait for Retry-After, then retry. |
organization | The plan does not include the requested feature or capacity. | Change the request or upgrade. Retrying will not help. |
source | A system you own failed: the Spec URL, the connected repository, or the package registry. | Check that system, then retry or follow the code's recovery. |
api | Typeship failed. | Follow retryable, and report the request_id if it persists. |
Request errors
Type request.
Input
| Code | Status | Retry | What to do |
|---|---|---|---|
input_missing | 400 | No | Supply the required field named by field. |
input_type_invalid | 400 | No | Send the value at field with the JSON type the schema requires. |
input_format_invalid | 400 | No | Send the value at field in the required format, such as an ID with the right prefix. |
input_too_long | 400 | No | Shorten the value at field, or send fewer items. |
input_too_short | 400 | No | Lengthen the value at field, or send more items. An empty string counts as too short. |
input_duplicate | 400 | No | Remove the repeated items from the array at field. |
input_unknown | 400 | No | Remove the field the operation does not accept. Misspelled fields are rejected, not ignored. |
input_invalid | 400 or 422 | No | Correct the value at field. Covers enum, range, and shape problems, invalid JSON (field: ""), and values that parse but cannot be used. |
query_param_invalid | 400 | No | Remove the unsupported or repeated query parameter. |
cursor_invalid | 400 | No | Restart the list without cursor, keeping the same filters for every page. |
method_not_allowed | 405 | No | Use a method from the Allow response header. |
resource_not_found | 404 | No | Check the ID with the matching list operation. A resource in another Organization also returns this. |
spec_invalid | 422 | No | Fix the Spec using the message, which quotes the generator. Warnings never cause this. See Spec errors and warnings. |
spec_too_large | 413 | No | The message gives the Spec's size and the limit. Send an inline Spec over 4 MB with spec.url; reduce a Spec over 10 MB across all of its documents. |
repository_provider_unsupported | 422 | No | Use a GitHub repository. |
State
| Code | Status | Retry | What to do |
|---|---|---|---|
target_busy | 409 | Yes | The Target is publishing. Wait for it to finish, then retry. |
targets_inactive | 409 | No | Create a Target or set one to status: active, then generate again. |
resource_changed | 409 | Yes | Get the current resource, reconcile, and retry. Use a new Idempotency-Key for the revised request. |
precondition_failed | 412 | No | The ETag in If-Match is stale and nothing was saved. Get the resource again and retry with its new ETag. |
delivery_conflict | 409 | No | Another Target owns that repository directory. Choose a different repository or directory. |
delivery_exists | 409 | No | The Target already has a Delivery of that type. Update or delete it instead. |
resource_has_dependencies | 409 | No | Disable the Target to keep its release history, or close its open Draft before deleting it. |
Versions and releases
| Code | Status | Retry | What to do |
|---|---|---|---|
version_invalid | 422 | No | Use a SemVer version such as 1.2.0 or 2.0.0-beta.1. |
version_occupied | 409 | No | That version is already released. Choose an unused version. |
version_too_low | 422 | No | Choose a version above the previous release that meets the Draft's version.bump_required. |
target_already_released | 409 | No | Adoption needs a Target with no release history. |
adoption_unverified | 422 | No | The repository tag and published package do not match the version. Check them, then retry adoption. |
Publishing
| Code | Status | Retry | What to do |
|---|---|---|---|
publication_disabled | 409 | No | Turn on publish_on_merge on the Target's repository Delivery. |
publication_not_retryable | 409 | No | No Publication is queued or failed. Get the release to check each Publication's status. |
publication_recovery_unavailable | 409 | No | The release predates reproducible publishing. Merge a new Draft to create a release that can be retried. |
Drafts
| Code | Status | Retry | What to do |
|---|---|---|---|
no_draft | 409 | No | The open Draft has no pending change. Generate the Target first. |
draft_merged | 409 | No | The Draft is final. Get the Target and use the Draft named by its draft_id. |
customization_conflict | On a Draft | No | Resolve each conflicted file against the Draft's head_sha. Typeship continues after the last decision. |
history_recovery_required | On a Draft | No | The default branch history was rewritten. Review the affected files and approve recovery. |
checks_failed | On a Draft | No | A required check failed on head_sha; the message names it. Open its url in checks, fix the package or check, and push. |
checks_unavailable | On a Draft | No | A required check could not run. Restore it, then make a reviewed Target change so it runs again. |
draft_title_invalid | On a Draft | No | Edit the pull request title so it describes the change. |
Go SDK dependencies
| Code | Status | Retry | What to do |
|---|
Authentication errors
Type auth.
| Code | Status | Retry | What to do |
|---|---|---|---|
auth_required | 401 | No | Send an API key or OAuth access token in Authorization: Bearer. |
api_key_invalid | 401 | No | The key is invalid, revoked, or expired, or its Organization is gone. Get an active key; revoked keys cannot be restored. |
token_invalid | 401 | No | The OAuth access token is invalid or expired. Sign in again. |
organization_required | 403 | No | Reconnect and choose an Organization during OAuth consent. |
insufficient_scope | 403 | No | Reconnect and approve the scope named by WWW-Authenticate, or use an Organization API key. |
role_insufficient | 403 | No | The operation is admin-only. Ask an Organization admin; another scope will not help. |
Idempotency errors
Type idempotency.
| Code | Status | Retry | What to do |
|---|---|---|---|
idempotency_key_invalid | 400 | No | Send a key of 1 to 255 visible ASCII characters, or omit the header. |
idempotency_key_reused | 409 | No | The key was used with different input. Resend the original request or use a new key. |
idempotency_key_in_use | 429 | After Retry-After | The original request is still running. Retry with the same key to receive its response. |
Rate limit errors
Type rate_limit.
| Code | Status | Retry | What to do |
|---|---|---|---|
rate_limit_exceeded | 429 | After Retry-After | Wait the Retry-After seconds. See Rate limits. |
Organization errors
Type organization.
| Code | Status | Retry | What to do |
|---|---|---|---|
feature_not_available | 402 | No | The plan lacks the feature named by field, such as the webhook relay. Upgrade, or send the request without it. |
quota_exceeded | 402 | No | A plan quota is full. Free includes one Project and one active Target per generator. Upgrade, or delete a Project or disable a Target. |
Source errors
Type source.
| Code | Status | Retry | What to do |
|---|---|---|---|
spec_unreachable | 400, 422, or 502 | Follow retryable | The Spec URL or a referenced document could not be read. Check the URL, access, and response. |
repository_disconnected | 409 | No | Reconnect the Target's GitHub repository. |
repository_unavailable | 502 | Yes | GitHub failed and nothing was saved. Retry; if it persists, check the repository connection and workflow. |
publication_failed | On a Publication | No | Open run_url, fix the workflow or registry failure, then retry the release. |
API errors
Type api.
| Code | Status | Retry | What to do |
|---|---|---|---|
follow_up_failed | 502 | Yes | Your change was saved, but follow-up work did not finish. Get the resource and repeat the same request. See saved-update recovery. |
regeneration_failed | 502 | Follow retryable | Your version selection was saved but generation failed. If retryable, repeat the selection; otherwise fix the cause shown in the Generation. |
api_error | 500 or 502 | Once, with backoff | Typeship failed. Retry once, and report the request_id if it persists. Older recorded failures without a known cause also use this code, with retryable: false. |
Errors on resources
Generations, Publications, and Drafts carry an errors array in the same entry shape. On a Generation or Publication each entry also has a required phase:
| Phase | Meaning |
|---|---|
spec | Typeship could not read or process the Spec. |
generation | Typeship could not generate the Target. |
delivery | Typeship generated the files but could not complete repository delivery. |
publication | Typeship could not start or complete publishing an accepted release. |
{
"id": "gen_7h2p5d9c3m8w1k6q",
"object": "generation",
"status": "completed",
"errors": [
{
"type": "source",
"code": "repository_disconnected",
"phase": "delivery",
"message": "Reconnect the GitHub repository parcel-example/parcel-client.",
"retryable": false,
"suggested_action": "Reconnect the Target's GitHub repository, then retry the operation.",
"docs_url": "https://typeship.dev/docs/typeship-api/errors#repository_disconnected"
}
]
}A completed Generation can carry delivery errors; its files remain available. An empty errors array means no recorded failure. On a recorded failure, retryable describes that failure's cause: true means a new generation or publishing attempt can succeed unchanged, which you start with the operation in suggested_action. Getting the resource does not retry it, and replaying the original Idempotency-Key returns the cached response, so use a new key. When every Target in a Project generation fails, the request itself returns an error envelope with each failure's target_id.
A Draft's errors lists what blocks it while its status is action_required, one entry per finding, such as each failed required check.
Request IDs
Every response carries a server-generated Request-Id header, and every JSON body repeats it as request_id. Responses without a JSON body, such as package downloads, HEAD responses, and redirects, carry it only in the header. Typeship ignores a caller-supplied Request-Id. Include the ID when you report a problem:
curl -sS https://typeship.dev/api/v1/organization \
-H "Authorization: Bearer $TYPESHIP_TOKEN" -o response.json
jq -r '.request_id' response.json