ruvnet/ruflo · error · Error

Web3.storage upload failed: ${response.status} ${error}

Error message

Web3.storage upload failed: ${response.status} ${error}

What it means

uploadToWeb3Storage() POSTs a multipart body to `${endpoint}/upload` (default https://api.web3.storage/upload) with an Authorization: Bearer token. When the HTTP response is not ok, the response body text is appended to the status code and thrown. So this is the upstream API rejecting the request: 401 for a bad/expired token, 4xx for malformed payloads, 5xx for outages. Note web3.storage's classic upload API has been deprecated/sunset by Protocol Labs, so non-200s are now common with old tokens.

Source

Thrown at v3/@claude-flow/cli/src/transfer/ipfs/upload.ts:115

    Buffer.from(`--${boundary}\r\n`),
    Buffer.from(`Content-Disposition: form-data; name="file"; filename="${name}"\r\n`),
    Buffer.from(`Content-Type: application/json\r\n\r\n`),
    content,
    Buffer.from(`\r\n--${boundary}--\r\n`),
  ]);

  const response = await fetch(`${endpoint}/upload`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': `multipart/form-data; boundary=${boundary}`,
    },
    body,
  });

  if (!response.ok) {
    const error = await response.text();
    throw new Error(`Web3.storage upload failed: ${response.status} ${error}`);
  }

  const result = await response.json() as { cid: string };
  const cid = result.cid;
  const gateway = options.gateway || 'https://w3s.link';

  console.log(`[IPFS] Upload complete!`);
  console.log(`[IPFS] CID: ${cid}`);

  return {
    cid,
    size: content.length,
    gateway,
    pinnedAt: new Date().toISOString(),
    url: `${gateway}/ipfs/${cid}`,
  };
}

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Read the status code in the message: 401/403 means regenerate the token and update WEB3_STORAGE_TOKEN
  2. If it is a persistent failure on a legacy token, migrate providers — pinata (PINATA_API_KEY/PINATA_API_SECRET) or a local IPFS node (IPFS_API_URL) are built-in alternatives
  3. For 5xx, retry after a backoff interval
  4. Confirm the endpoint if you set a custom one via options.endpoint

Example fix

// before
const result = await uploadToIPFS(content, { provider: 'web3' }); // 401 -> Web3.storage upload failed: 401 ...

// after
let result;
try {
  result = await uploadToIPFS(content, { provider: 'web3' });
} catch (e) {
  if (/Web3\.storage upload failed/.test(String(e?.message))) {
    result = await uploadToIPFS(content, { provider: 'pinata' }); // fallback provider
  } else throw e;
}
Defensive patterns

Strategy: fallback

Try / catch

const isWeb3Failure = (e: unknown) => /Web3\.storage upload failed: (\d+)/.test(String((e as Error)?.message));

try {
  result = await uploadToIPFS(content, { provider: 'web3' });
} catch (e) {
  if (!isWeb3Failure(e)) throw e;
  const status = Number(/: (\d+)/.exec(String((e as Error).message))?.[1]);
  if (status === 401 || status === 403) throw new Error('web3.storage token invalid — refresh WEB3_STORAGE_TOKEN'); // not retryable
  result = await uploadToIPFS(content, { provider: 'pinata' }); // 4xx/5xx -> fall back to another provider

Prevention

When it happens

Trigger: Uploading with an expired or revoked WEB3_STORAGE_TOKEN (401); uploading after the web3.storage classic service was deprecated (the endpoint no longer accepts uploads); oversized or malformed content producing 4xx; transient 5xx during an API incident.

Common situations: Tokens that worked historically now failing because the service shut down; free-tier limits hit; copied credentials from another account; corporate proxies returning 403/502 on the POST.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/8a7ba18ff2430299. Report an issue: GitHub.