valve_

Handle errors

Tell valve's own failures apart from your job's errors and respond to each one.

A job's promise rejects for one of two reasons:

  • Your function failed. valve passes the error through unchanged.
  • valve could not run the job. valve rejects with a ValveError that has a code.

run never throws synchronously. Invalid options passed to createValve throw at creation time with code INVALID_OPTIONS.

Narrow with isValveError

import { isValveError } from 'valvejs';

try {
  await crm.run(() => fetchContact(id));
} catch (error) {
  if (isValveError(error, 'QUEUE_FULL')) return shedLoad();
  if (isValveError(error, 'MAX_WAIT_EXCEEDED')) return respondBusy();
  if (isValveError(error)) {
    logger.warn({ code: error.code, valve: error.valveId }, error.message);
  }
  throw error; // Your job's own error, unchanged.
}

ValveError exposes code, valveId, message, and, when there is an underlying error, cause.

What to do for each code

CodeWhy it happenedWhat to do
INVALID_OPTIONSAn option or argument failed validation.Fix the configuration. The message names the option.
COST_EXCEEDS_LIMITThe job's cost is larger than a rule's burst or limit.Lower the cost or raise burst. The job could never start.
QUEUE_FULLmaxQueue jobs are already waiting.Shed load, return 429 or 503 to your caller, or raise maxQueue.
MAX_WAIT_EXCEEDEDThe job waited longer than maxWait.Fail fast to your caller, or retry later.
ABORTEDThe job's signal aborted before it started.Usually nothing. cause holds signal.reason.
CLOSEDThe valve was closed before the job started.Expected during shutdown.
STORE_UNAVAILABLEThe store failed and onStoreError is 'throw'.Retry later, or switch to the default 'wait' policy.
LEASE_LOSTA global concurrency lease could not be renewed.Treat the job's outcome as unknown. Make the work idempotent.

Store outages

Choose how valve behaves when Redis is down or slow. A store call that exceeds the Redis store's timeout (default '2s') counts as a failure.

createValve({ id: 'crm', rate, store, onStoreError: 'wait' });
  • 'wait' (default) keeps jobs queued and retries with backoff until each job's maxWait. Pair it with maxWait so callers are not stuck forever.
  • 'throw' rejects affected jobs with STORE_UNAVAILABLE.
  • 'open' runs jobs without shared rate rules while the store is down. Local concurrency still applies. Only use it when exceeding the provider's quota is better than not calling it.

Every store failure emits a store-error event, whatever the policy.

A reported shared Redis configuration mismatch rejects affected jobs with INVALID_OPTIONS under every policy. It cannot fail open or retry indefinitely as an outage. Restore matching settings or follow Changing a shared quota. An outage or timeout can hide the mismatch response. With 'open', those jobs run without shared admission checks, as they do during any other store outage.

Errors in callbacks

valve catches errors thrown by event listeners, feedback readers, and observe mappers. It reports them as warning events instead of failing the job. Subscribe to warning so these do not go unnoticed:

crm.on('warning', ({ message }) => logger.warn(message));

On this page