actualbudget/actual · error
Forbidden
Error message
Forbidden
What it means
After following redirects, getAccounts checks for an HTTP 403 and throws 'Forbidden'. SimpleFIN returns 403 when the access key's credentials are rejected — most commonly after the user revoked access via the SimpleFIN Bridge, or the key/password was invalidated. The server surfaces this instead of a parse error so callers know it's an authorization problem.
Source
Thrown at packages/sync-server/src/app-simplefin/app-simplefin.js:469
});
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;
results.errors = {};
return results;
} catch (e) {
console.log(`Error parsing JSON response: ${text}`);
throw e;
}
}
View on GitHub (pinned to d4334cb6e6)
Solutions
- Generate a new access key at https://bridge.simplefin.org/auth/login and update it in the SimpleFIN bank-sync settings.
- Log in to the SimpleFIN Bridge dashboard and re-authorize access for the integration.
- Confirm the stored access key is current (not an older revoked one) and re-run setup.
- If access was intentionally revoked, remove the SimpleFIN account from Actual instead of retrying.
Example fix
// before // syncing with stale key await getAccounts(oldAccessKey, startDate, endDate); // 403 // after // re-setup with fresh key from bridge.simplefin.org const newKey = await claimAccessKey(newToken); await getAccounts(newKey, startDate, endDate);
Defensive patterns
Strategy: try-catch
Try / catch
try {
const accounts = await getAccounts(accessKey, startDate, endDate);
} catch (e) {
if (e.message === 'Forbidden') {
// access revoked — prompt user to re-authenticate via bridge.simplefin.org
promptSimplefinReauth();
return;
}
throw e;
} Prevention
- On 403, always re-run the setup/claim flow rather than retrying the same key.
- Don't revoke access in the SimpleFIN Bridge while integrations still use it.
- Surface auth errors to users distinctly from network errors.
When it happens
Trigger: GET to the SimpleFIN accounts/transactions URL (after redirects) returns 403: revoked access key, deleted SimpleFIN account, expired credentials, or the key no longer matching the server.
Common situations: User clicked 'revoke' in the SimpleFIN Bridge dashboard while Actual still holds the old key; account password rotated at SimpleFIN; key regenerated and the old one invalidated.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Invalid access key
- SimpleFIN claim failed with HTTP ${response.status}
- INVALID_ACCESS_TOKEN
- Authentication required. Set --password/--session-token, ACT
- Authentication required. Provide --password or --session-tok
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/3f579db6405f167d.
Report an issue: GitHub.