railwail SDK · errors

RailwailError

railwail@1.0.0 throws a RailwailError for every error answer of the API, with the HTTP status, error type and code. Timeouts, network failures and non-JSON answers throw their own errors, and two situations are not errors at all.

Class
RailwailError extends Error
Fields
status · type · code · message
Timeout
AbortError after 120 s
Headers
not exposed in 1.0.0

Import

TypeScript
import railwail, { RailwailError } from "railwail";

Properties

PropertyTypeValue
statusnumberHTTP status of the answer (400–503).
codestringThe API's error code, e.g. invalid_api_key, trial_limit. Switch on this. "unknown" when the answer had no error body.
typestringThe error category (list below).
messagestringHuman-readable; for example exactly "Invalid API key". It can name the missing scope or the IP address the API saw.
name"RailwailError"Set by the constructor; instanceof RailwailError works too.

What throws what

SituationYou getNotes
The API answers with an error (4xx/5xx JSON)RailwailErrorstatus, type, code from the body.
Your timeout runs out (default 120 s)AbortErrorerr.name === "AbortError". The run continues on the server and is billed; raise railwail(key, { timeout }) for long answers.
No connectionTypeErrorfetch failed, from Node's fetch.
A non-JSON answer (e.g. an HTML 502 page from the edge)SyntaxErrorFrom parsing the body. Retry like a 502.
An image run takes longer than 85 sno errorHTTP 202: data[0].url is null; at runtime the item also has job_id for rw.job().
A chat run fails after more than 25 sno errorThe status was already 200, so the result has error instead of choices. Check for it (example).

Error types

typeUsed for
authentication_error401 invalid_api_key; also 403 forbidden on jobs/{id}
permission_error403 insufficient_scope, ip_not_allowed
invalid_request_error400 and 404 codes, 499; 503 model_unavailable when a model has no price
insufficient_credits_error402 insufficient_credits, spending_limit_reached
spending_limit_error402 monthly_limit_exceeded (videos, audio)
rate_limit_error429 rate_limit_exceeded, trial_limit, upstream_rate_limited
api_error500, 502, 503; also the SDK's own fallback (code "unknown")

Error Handling

errors.mts
import railwail, { RailwailError } from "railwail";

const rw = railwail(process.env.RAILWAIL_API_KEY!);

try {
  const reply = await rw.run("gpt-4o-mini", "Hello!", { max_tokens: 300 });
  console.log(reply);
} catch (err) {
  if (!(err instanceof RailwailError)) throw err; // AbortError, TypeError, SyntaxError: see above

  // Switch on the code, not on the status: 402 and 429 each have several causes.
  switch (err.code) {
    case "invalid_api_key":
      console.error("Check RAILWAIL_API_KEY");
      break;
    case "insufficient_scope":
    case "ip_not_allowed":
      console.error(err.message); // names the missing scope or the IP address the API saw
      break;
    case "insufficient_credits":
    case "trial_limit":
      console.error("Top up: https://railwail.com/dashboard/billing");
      break;
    case "spending_limit_reached":
      console.error("Monthly limit: https://railwail.com/dashboard/settings/spend-limits");
      break;
    case "rate_limit_exceeded":
    case "upstream_rate_limited":
      console.error("Slow down and retry");
      break;
    case "model_not_found":
    case "model_not_supported":
    case "model_unavailable":
      console.error("Pick another model: https://railwail.com/models");
      break;
    default:
      console.error(err.status, err.type, err.code, err.message);
  }
}

Common Error Codes

The codes the SDK's methods can meet (chat, image, embed, models, job). Each links to cause, fix and retry advice in the full reference.

  • 400
    invalid_json

    Send a JSON object with Content-Type: application/json.

  • 400
    invalid_request

    Fix the field named in param. One request per completion instead of n > 1; tools / tool_choice instead of functions.

  • 400
    validation_failed

    Remove or correct the fields listed in details.

  • 400
    provider_rejected_input

    Split long texts or send fewer inputs per request. The run was refunded.

  • 400
    invalid_category

    Use one of the values the message lists, or leave the filter out.

  • 400
    model_not_supported

    Use a chat model from OpenAI, Anthropic, Google or DeepSeek, or run the model on its model page.

  • 400
    provider_rejected_request

    Read the message, shorten the input or adjust the parameters.

  • 401
    invalid_api_key

    Send Authorization: Bearer rw_live_… with an active key.

  • 402
    insufficient_credits

    Top up your balance, or lower max_tokens.

  • 402
    spending_limit_reached

    Raise or remove the limit under Spend limits, or wait for the next month.

  • 403
    insufficient_scope

    Create a new key with that scope or "all" (a key's scopes cannot be edited after creation).

  • 403
    ip_not_allowed

    Add the address (or its CIDR block) to a new or rotated key, or call from an allowed host.

  • 403
    forbidden

    Poll only jobs created with a key of your account.

  • 404
    model_not_found

    Use a slug from the model pages or GET /api/v1/models.

  • 404
    job_not_found

    Use the id from X-Railwail-Job-Id, the part after chatcmpl-, or job_id in a 202 answer.

  • 413
    too_many_embedding_values

    Send fewer inputs per request, or ask for fewer dimensions. Nothing was charged.

  • 429
    rate_limit_exceeded

    Wait X-RateLimit-Reset seconds. There is no Retry-After header.

  • 429
    api_key_limit_exceeded

    Do not retry before the instant in X-Key-Limit-Reset (ISO 8601, UTC: the next 00:00 UTC for the daily limit, the 1st of the next month for the monthly one); X-RateLimit-Reset does not apply. Or raise the key's limit under Limits & alerts.

  • 429
    trial_limit

    Top up once to lift the trial rules, or lower max_tokens.

  • 429
    upstream_rate_limited

    Retry with exponential backoff, or use another model.

  • 500
    internal_error

    Retry once. If it persists, write to info@railwail.com with the time of the request (and for chat the X-Railwail-Job-Id).

  • 502
    upstream_error

    Retry with exponential backoff.

  • 503
    model_unavailable

    Choose another model; retry later.

  • 504
    provider_timeout

    Retry, or send fewer inputs per request.

Every code links to its cause, fix and retry advice in the full reference.

Retrying

1.0.0 has no automatic retries. Retry only the codes a retry can fix; everything else fails again. For the exact wait of rate_limit_exceeded you need the X-RateLimit-Reset header, which the SDK does not expose: the fetch-based helper reads it.

retry.mts
import railwail, { RailwailError } from "railwail";

const RETRYABLE = new Set([
  "rate_limit_exceeded",
  "upstream_rate_limited",
  "upstream_error",
  "model_unavailable",
  "internal_error",
]);

async function withRetry<T>(fn: () => Promise<T>, tries = 4): Promise<T> {
  for (let attempt = 1; ; attempt++) {
    try {
      return await fn();
    } catch (err) {
      if (!(err instanceof RailwailError) || !RETRYABLE.has(err.code) || attempt >= tries) throw err;
      // RailwailError carries no headers, so back off exponentially with jitter.
      await new Promise((resolve) => setTimeout(resolve, 2 ** attempt * 500 + Math.random() * 250));
    }
  }
}

const rw = railwail(process.env.RAILWAIL_API_KEY!);
const res = await withRetry(() =>
  rw.chat("gpt-4o-mini", [{ role: "user", content: "Hello" }], { max_tokens: 300 }),
);
console.log(res.choices[0].message.content);
RailwailError — Railwail Docs