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.

Jeder Code führt zu Ursache, Lösung und Wiederholregel in der vollständigen Referenz.

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