Farther ShoreDocs
Go to Farther Shore
Understand gateway behavior
Consuming the API
Monetary admission
Usage limits
Gate API routes by plan
Diagnose a denied request
Response & deny codes
Platform deny wire-codes (FSDENYCODES)Retryability is decision-specificUpgrade affordance — limitCodeRuntime verification statusesOrigin availability — originunavailable and origintimeoutOther stable operational codesHTTP status taxonomyHow to debug a denial
gateway HTTP contracts
Status
Docs/Reference/Response & deny codes

Response & deny codes

HTTP statuses and the canonical platform deny wire-codes.

When the platform denies a request it returns stable base fields: a human-readable error plus a machine-readable code. Limit decisions may also include limitCode and an _fs envelope with reaction, retry safety, origin, decision id, and limit metadata. Branch on typed SDK helpers and code, never the message. The frontend SDK exposes the canonical gateway vocabulary as FS_DENY_CODES.

json
{ "error": "Rate limit reached for this key.", "code": "rate_limited" }
ts
import {
  FartherShoreApiError,
  FS_DENY_CODES,
} from "@farthershore/farthershore-js";

try {
  await fs.route.get("/v1/cron-jobs");
} catch (err) {
  if (err instanceof FartherShoreApiError) {
    switch (err.code) {
      case FS_DENY_CODES.credit_exhausted:
        return showFundingExhausted();
      case "route_not_enabled":
        return promptUpgrade();
    }
  }
}

Platform deny wire-codes (FS_DENY_CODES)

The canonical deny code values. Grouped by concern; the HTTP status the platform returns alongside each is in the table.

codeHTTPMeaning
limit_exceeded402A usage/quota limit on a metered dimension is reached.
rate_limited429A per-window rate limit is reached. Retryable.
credit_exhausted402On a block plan, available funding cannot cover the request's evaluated economic maximum (owner: the monetary reservation).
credit_state_unavailable503A block plan's funding projection is absent or unreadable — fails closed before allocation. Retryable.
enforcement_denied402A consume-phase batch enforcement check denied the request.
commercial_release_unprovable503The per-business commercial-release bundle is absent, incomplete, hash-invalid, scope-mismatched, or the emergency budget is exhausted. Retryable.
admission_descriptor_unavailable503The route's admission descriptor is missing, corrupt, or from the wrong release / rating context. Retryable.
admission_descriptor_no_admissible_tuple503No conditional dimension tuple is consistent with the request. Retryable.
invalid_admission_knob422The client's output knob (max_tokens and aliases) is malformed or has conflicting aliases. Fix the request.
admission_bound_exceeded422The client's output knob exceeds the route's declared bound and cannot be safely rewritten. Lower the knob.
limit_allocator_unavailable503A limit check was transiently unavailable. Retryable.
route_not_enabled403The active plan does not grant the matched route identity.
invalid_entitlement_shape503The resolved plan access failed schema validation. Retryable.
unsupported_constraint_schema503A limit rule used an unsupported schema. Retryable.
enforcement_error500Enforcement hit an unexpected error.
enforcement_dependency_unavailable503An enforcement dependency was transiently unavailable. Retryable.
concurrency_limit_exceeded429The plan's concurrent-request cap is reached. Retryable.
concurrency_context_unavailable503Concurrency context couldn't be read. Retryable.
concurrency_coordinator_unavailable503The concurrency coordinator was unavailable. Retryable.
key_expired401The API key has expired.
credential_revoked401The credential was withdrawn (revoked, or its owning subscription, plan or business was removed). Not retryable; a new credential is required.
credential_rotated401The credential was superseded by a rotation. Re-read the stored secret and retry with the new one.
credential_env_reset401The preview environment was rebuilt from a new contract, which deletes its subscriptions and their keys. Re-subscribe, or bootstrap a new persona.
permission_denied403The member's resolved permissions do not satisfy the route requirement.
permission_unresolved403RBAC is enabled but the credential has no usable permission claim.
geo_context_unavailable503Geo context couldn't be resolved. Retryable.
geo_blocked403The request origin is in a blocked region.
geo_not_allowed403The request origin isn't in the allow-list.
resource_count_limit_exceeded402A counted-resource cap (e.g. cron_jobs) is reached. Core atomically authorizes the create and the gateway relays the denial.
post_stream_overspend402Previously reported streaming usage crossed a blocking quota; later requests remain locked out until reset.
resolver_rate_limited429An internal resolver was rate-limited. Retryable.
resolver_unavailable503An internal resolver was unavailable. Retryable.
credential_resolver_miss_rate_limited429Credential-resolver miss path was rate-limited. Retryable.
request_too_large413A per-request capacity/payload ceiling was exceeded; modify the request (capacity class, not retryable).
provider_throttled503A relayed upstream-provider throttle (adaptive class, limitOrigin: provider). Retryable.

Retryability is decision-specific

Use the public guards on the caught error, not a raw status or code. _fs.reaction and _fs.retrySafe are authoritative when present, so a structured decision can make a nominal 429/503 unsafe to replay. Only when no structured envelope exists does the SDK fall back to the transient code/status classification below.

ts
import { isRetryable, isThrottled } from "@farthershore/farthershore-js/errors";

try {
  await fs.route.get("/v1/cron-jobs");
} catch (err) {
  if (!isReplaySafeOperation()) throw err;
  if (isThrottled(err)) return backOffAndRetry(); // honor Retry-After
  if (isRetryable(err)) return retryWithBackoff(); // transient fallback
  throw err;
}

Fallback-transient codes are limit_allocator_unavailable, rate_limited, invalid_entitlement_shape, unsupported_constraint_schema, enforcement_dependency_unavailable, concurrency_limit_exceeded, concurrency_context_unavailable, concurrency_coordinator_unavailable, geo_context_unavailable, resolver_rate_limited, resolver_unavailable, credential_resolver_miss_rate_limited, provider_throttled.

limit_exceeded and credit_exhausted are not retryable — the limit won't clear by retrying. Surface an upgrade or funding affordance instead (see the limitCode field below). invalid_admission_knob and admission_bound_exceeded (422) mean the request itself must change.

Never automatically replay a mutation unless its idempotency contract proves the same request cannot duplicate the effect.

Upgrade affordance — limitCode

A limit deny also carries a separate limitCode field (an upgrade-affordance value, not a wire code) telling the UI what kind of limit was hit. The fixed values:

limitCodeHit
quotaAn included-usage / hard-cap quota.
rate_limitA per-window rate limit.
creditFunding exhaustion on a block plan (credit_exhausted).
resource:<name>A counted-resource cap, e.g. resource:cron_jobs (open family — match by the resource: prefix).

Runtime verification statuses

These come from the upstream's @farthershore/backend, not the platform deny path — when fs.verifyRequest() / fs.middleware() rejects a request the platform forwarded. Verification is fail-closed: every failure maps to one status.

HTTPWhen
401Any verification failure — missing / malformed / bad-signature / stale / clock-skew / wrong-route / body-hash-mismatch / replayed-nonce / unknown-kid / jwks-unavailable.
403Verified principal is the wrong subject or credential surface, or fails an application permission check (member_subject_required, service_subject_required, surface_not_allowed).
413The request body exceeds MAX_BODY_BYTES.

FartherShoreError.code carries the precise runtime reason (for example, invalid_token or jwks_unavailable). statusForCode(code) supplies the default mapping and explicitly handles body_too_large and surface_not_allowed; subject and permission helpers construct their own 403 errors. There is no fail-open branch.

Origin availability — origin_unavailable and origin_timeout

These two codes describe the hop between the gateway and your backend. They are always the platform's own envelope — the gateway never relays your hosting provider's error page (Railway's Application not found, a raw HTML 502), so a caller can always tell "the origin is not there" from "your application said no". Neither is ever billed to the customer, and any reservation the request took is released.

HTTPcodeWhen
503origin_unavailableThe environment has no usable backend origin, or the origin could not be reached — connection refused, DNS/TLS failure, or a hosting-edge 5xx page.
504origin_timeoutThe origin did not produce response headers within the route's time-to-first-byte budget.

Both carry Retry-After and an _fs envelope with { "retrySafe": true, "reaction": "backoff_retry" }:

json
{
  "error": "Origin unavailable",
  "code": "origin_unavailable",
  "_fs": { "retrySafe": true, "reaction": "backoff_retry" }
}

Your backend's OWN responses are never rewritten. A JSON body your application returns — including a 404 or a 503 — is relayed verbatim; only a signature-less hosting-edge error page is reclassified. A provider cap your backend reports (for example 413 {"error":"too_many_pages"}) keeps its body too: the platform merges its _fs telemetry into your document rather than replacing it.

Other stable operational codes

Not every stable code is a member of the closed gateway-denial catalog. origin_unavailable is a gateway routing failure: the selected environment has no usable backend origin and returns 503 without falling back to production. Backend verification and authorization also use stable runtime codes such as context_unverified, principal_required, member_subject_required, service_subject_required, and surface_not_allowed. Diagnose those at the signature/principal/application boundary rather than adding them to FS_DENY_CODES. A usage payload from an outdated @farthershore/backend that lacks the served-identity block receives a permanent 410/422 unsupported_usage_schema with expected_schema_version and received_schema_version — upgrade the SDK; the event is never queued, quarantined, or backfilled.

HTTP status taxonomy

How the statuses map to categories across both surfaces:

HTTPCategoryTypical codes
401Authenticationkey_expired, credential_revoked / credential_rotated / credential_env_reset, runtime verification failures
402Funding / quotacredit_exhausted, limit_exceeded, post_stream_overspend, enforcement_denied, resource create limits
403Authorizationroute_not_enabled, permission denies, geo denies, resource pre-request limits
413Payloadrequest_too_large (oversized body / capacity)
422Admission knobinvalid_admission_knob, admission_bound_exceeded (modify the request)
429Rate / throughputrate_limited, concurrency_limit_exceeded (customer wait-for-slot)
503Origin availabilityorigin_unavailable (no usable / unreachable backend origin)
504Origin availabilityorigin_timeout (no response headers within the route budget)
500Runtimeenforcement_error
503Transient runtime*_unavailable, *_rate_limited, commercial_release_unprovable (retryable, fail-closed)

How to debug a denial

  1. Read the code (not the message).
  2. Call isRetryable(err) and require replay-safe operation semantics. Honor the structured decision and Retry-After before retrying.
  3. If it's a limit (limit_exceeded / credit_exhausted / resource_count_limit_exceeded), read limitCode and surface an upgrade or funding affordance.
  4. If it's a 422 admission code, the client's output knob is malformed or above the route's declared bound — see Monetary admission.
  5. If it's a 401 from your own upstream, it's a verification failure — check FartherShoreError.code and that FS_RUNTIME_TOKEN is current.
  6. Confirm the subscriber's key, subscription, and plan limits.
PreviousDiagnose a denied requestNextgateway HTTP contracts

On this page

Platform deny wire-codes (FSDENYCODES)Retryability is decision-specificUpgrade affordance — limitCodeRuntime verification statusesOrigin availability — originunavailable and origintimeoutOther stable operational codesHTTP status taxonomyHow to debug a denial