Radondocs

Failover

An ordered chain of storage providers, tried in turn until one succeeds — what fails over, what doesn't (getUrl and list), and how AllProvidersFailedError works.

Pro feature

Multi-provider failover is a Radon Pro feature. Configuring a failover chain requires a licenseKey and await storage.init() — otherwise you get LicenseRequiredError.

Failover gives you an ordered chain of providers. Each operation tries the first provider; if it fails, Radon transparently retries the next, and so on, returning the first success. It's redundancy for your storage layer — if S3 has an outage, writes and reads keep flowing to your backup.

The chain

Set failover to an ordered list of provider slugs. Each must also appear in providers.

S3 primary, R2 backup
const storage = new RadonStorage({
  providers: {
    s3: { bucket: "primary", region: "us-east-1" },
    r2: { bucket: "backup", accountId: process.env.CF_ACCOUNT },
  },
  failover: ["s3", "r2"],                 // try s3, fall back to r2
  licenseKey: process.env.RADON_LICENSE_KEY,
});

await storage.init();

// Lands on the first provider that accepts it — s3, or r2 if s3 fails:
await storage.upload({ key: "a.png", body: buf });

The head of the chain (s3 here) becomes the default provider, so you don't need to set defaultProvider separately.

What fails over

Every read and write operation flows through the chain:

  • upload
  • download
  • delete
  • exists
  • getMetadata
  • copy

Each tries providers in order until one succeeds.

What does NOT fail over

getUrl and list use the primary provider only

getUrl() and list() never fail over — they always target the primary (head-of-chain) provider. Both are deterministic, per-provider operations: a signed URL is only valid for the store that minted it, and a listing is meaningful only for one bucket. Failing them over would produce dead links or merged, confusing results.

Resumable/multipart uploads also stay on the primary provider — a multipart session is bound to the store that opened it.

Bypassing the chain

Name a provider explicitly and that call skips failover entirely — it targets exactly that provider:

await storage.upload({ key: "a.png", body: buf }, { provider: "s3" }); // s3 only, no fallback

This is how you write to a specific store even when a chain is configured.

When every provider fails

If all providers in the chain fail, Radon throws AllProvidersFailedError. It carries each underlying error, in the order the providers were tried, so you can see exactly what went wrong where.

import { AllProvidersFailedError } from "@radonsdk/storage";

try {
  await storage.upload({ key: "a.png", body: buf });
} catch (err) {
  if (err instanceof AllProvidersFailedError) {
    for (const { provider, error } of err.errors) {
      console.error(`${provider} failed:`, error);
    }
  }
}
errors{ provider: string; error: unknown }[]

One entry per attempted provider, in chain order, each pairing the provider slug with the error it threw.

Failover vs. explicit backup

Failover retries on any failure — including a genuine client error like an object that doesn't exist. If provider A returns "not found" and provider B has the object, a download will succeed from B, which is usually what you want for redundancy. Keep this in mind for operations where "not found" is a meaningful answer: prefer naming the provider explicitly if you need to distinguish "missing on the primary" from "missing everywhere".

Next steps

On this page