UpstashRedis Errors - TundraSoft/tundra-connect GitHub Wiki

UpstashRedis Errors

UpstashRedis throws UpstashRedisError for local request validation and vendor responses — see errors/Base.ts and errors/UpstashRedisErrorCodes.ts.

import {
  UpstashRedisError,
  UpstashRedisErrorCodes,
} from '@tundraconnect/upstash-redis/errors';

const error = new UpstashRedisError('SERVICE_UNAVAILABLE', { status: 503 });
console.log(error.message);

Codes

Code Meaning
UNKNOWN_ERROR An unmapped HTTP status was returned, or an unknown code was supplied to the constructor.
CONFIG_INVALID_TOKEN auth is missing, isn't type: 'BEARER', or its token is blank/not a string. Thrown at construction time.
REQUEST_VALIDATION_ERROR A command array (built internally, or passed to execute()/pipeline()) failed local Guardian validation and was never sent — error.cause is a GuardianError.
INVALID_REQUEST A local business-rule check failed before any request was sent — SET's ex+px or nx+xx both set, or an empty key/value/field list for del/exists/hset/lpush/rpush. Carries the reason in error.getContextValue('reason').
AUTH_FAILED UpstashRedis returned HTTP 401 — the Bearer token is missing or invalid.
COMMAND_ERROR UpstashRedis returned HTTP 400. Upstash uses this single status for both a malformed request and a Redis command that failed (e.g. "ERR wrong number of arguments for 'get' command") — there is no separate status to tell the two apart, so this connect doesn't invent one either. The vendor's raw message is in error.getContextValue('reason').
METHOD_NOT_ALLOWED UpstashRedis returned HTTP 405 (only HEAD/GET/POST/PUT are supported).
RESPONSE_ERROR A success response's body failed schema validation.
SERVICE_UNAVAILABLE UpstashRedis returned a 5xx status.
TIMEOUT Upstash did not answer within the client timeout (timeoutSeconds in context).
NETWORK_ERROR fetch failed before any response (DNS, TLS, connection reset); the transport error is the cause.

Why 400 maps to one code, not two

Upstash's own documentation describes 400 Bad Request as covering both "a syntax error, an invalid/unsupported command is sent" and "command execution fails" — the same status, the same {"error": "..."} envelope, for both a malformed request and a Redis-level command error (verified directly against the vendor's docs). Since there's no way to reliably tell them apart from the HTTP layer, COMMAND_ERROR covers both, with the vendor's message attached for whoever needs to distinguish them by reading it.

The resolved code is also available as a public, readonly error.code property — branch on failure mode without matching against .message:

try {
  await client.get('foo');
} catch (error) {
  if (error instanceof UpstashRedisError && error.code === 'COMMAND_ERROR') {
    console.error(
      'Redis rejected the command:',
      error.getContextValue('reason'),
    );
  }
}

Use getContextValue() to read diagnostic metadata — see errors/Base.ts. The configured Bearer token is never included in any thrown error's message or context.

Transient failures

TIMEOUT, NETWORK_ERROR, SERVICE_UNAVAILABLE and RATE_LIMITED mean "no answer yet": retrying later can help. Every other code is a definite refusal or a misconfiguration that retrying will not fix. Branch on the readonly err.transient, which is true for exactly these four, rather than listing codes yourself. The set is also exported as UPSTASH_REDIS_TRANSIENT_CODES from @tundraconnect/upstash-redis/errors.

import { UpstashRedisError } from '@tundraconnect/upstash-redis/errors';

declare function callTheClient(): Promise<unknown>;

try {
  await callTheClient();
} catch (err) {
  if (err instanceof UpstashRedisError && err.transient) {
    // queue it and try again later
  }
  throw err;
}
Context Present on Meaning
timeoutSeconds TIMEOUT The deadline that was missed, in seconds.

Backing off after a 429

A rate-limited request throws RATE_LIMITED. Two context values tell you what to do next:

Context Meaning
retryAfterSeconds Seconds the vendor asked you to wait, parsed by RESTler from Retry-After (delta seconds or an HTTP-date), X-RateLimit-Reset-After, RateLimit-Reset, or an epoch-seconds X-RateLimit-Reset. undefined when the response carried none — never a guess.
retried Set only when maxRetryWait is configured: true if RESTler already waited once and was throttled again, false if it did not wait (the hint exceeded the cap, or there was no hint to wait on).

Nothing is retried by default. Opt in with two client options (seconds, each 0–120):

  • maxRetryWait — when a 429 carries a hint no longer than this, RESTler waits that long and retries once; otherwise it throws RATE_LIMITED immediately. 0 disables the retry.
  • defaultRetryWait — the wait used when a 429 carries no hint. Without it a hintless 429 is never retried: RESTler does not invent a delay. Only consulted when maxRetryWait is set, and still capped by it.

← Back to UpstashRedis

Rate limiting

Code Raised when
RATE_LIMITED HTTP 429. Read retryAfterSeconds from the error context and back off; never retry immediately.
⚠️ **GitHub.com Fallback** ⚠️