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
- Fix the redirect loop at the source (reverse proxy config): ensure the SimpleFIN server responds 200 directly at the configured base URL.
- Verify the base URL embedded in the access key points to the final, canonical https address and doesn't need rewriting.
- Test the access-key URL with curl -IL and resolve any repeated 3xx chain manually.
- 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
- Test the SimpleFIN base URL with curl -IL before configuring it.
- Avoid reverse-proxy rewrite rules that bounce the API path.
- Pin the canonical https URL in the access key.
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
- results.reason || results.error_code
- results.reason || results.error
- Failed to fetch catalog: ${response.statusText}
- Failed to fetch CSS from ${url}: ${response.status} ${respon
- ${text}
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/9470fb7a13ad7062.
Report an issue: GitHub.