actualbudget/actual · error

Too many redirects

Error message

Too many redirects

What it means

SimpleFIN's fetch helper follows redirects manually (up to MAX_REDIRECTS) so it can strip the Authorization header when a redirect crosses origins. If the hop counter reaches MAX_REDIRECTS before a non-3xx response arrives, it throws 'Too many redirects' to prevent an infinite redirect loop against the SimpleFIN API.

Source

Thrown at packages/sync-server/src/app-simplefin/app-simplefin.js:458

  // default cross-origin stripping) so the bridge credentials aren't leaked.
  const MAX_REDIRECTS = 5;
  let currentUrl = url.toString();
  let response;
  for (let hop = 0; ; hop++) {
    await assertUrlAllowed(currentUrl, { allowPrivateNetwork: true });

    response = await fetch(currentUrl, {
      method: 'GET',
      headers,
      redirect: 'manual',
    });

    const location = response.headers.get('location');
    if (response.status < 300 || response.status >= 400 || !location) {
      break;
    }
    if (hop >= MAX_REDIRECTS) {
      throw new Error('Too many redirects');
    }

    const nextUrl = new URL(location, currentUrl);
    if (nextUrl.origin !== new URL(currentUrl).origin) {
      delete headers.Authorization;
    }
    currentUrl = nextUrl.toString();
  }

  if (response.status === 403) {
    throw new Error('Forbidden');
  }

  const text = await response.text();
  try {
    const results = JSON.parse(text);
    results.sferrors = results.errors;
    results.hasError = false;

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Fix the redirect loop at the source (reverse proxy config): ensure the SimpleFIN server responds 200 directly at the configured base URL.
  2. Verify the base URL embedded in the access key points to the final, canonical https address and doesn't need rewriting.
  3. Test the access-key URL with curl -IL and resolve any repeated 3xx chain manually.
  4. Bypass intermediaries (proxy/CDN) or correct their rewrite rules to stop the bounce.

Example fix

// before (nginx)
return 301 https://$host$request_uri; // loops back to itself

// after (nginx)
proxy_pass http://sfin-backend;
# no self-redirect; serve API directly at https
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-flight: check the base URL resolves without a long redirect chain
const res = await fetch(baseUrl, { redirect: 'manual' });
if ([301,302,303,307,308].includes(res.status)) console.warn('base URL redirects; fix proxy config');

Try / catch

try {
  const accounts = await getAccounts(accessKey, startDate, endDate);
} catch (e) {
  if (e.message === 'Too many redirects') {
    throw new Error('SimpleFIN server/proxy redirect loop — fix reverse proxy or base URL');
  }
  throw e;
}

Prevention

When it happens

Trigger: The SimpleFIN base URL (from the access key) keeps responding with 3xx + Location headers beyond the redirect limit — e.g. a misconfigured custom base URL, a loop in the server's redirects, or an intermediate proxy bouncing between URLs.

Common situations: Self-hosted SimpleFIN bridge behind a reverse proxy with a redirect loop (http↔https or trailing-slash rewrites); wrong custom server URL configured; DNS/proxy issues causing repeated redirects.

Related errors


AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29). Data as JSON: /api/errors/9470fb7a13ad7062. Report an issue: GitHub.