actualbudget/actual · error
Authorization failed. You can close this window and try agai
Error message
Authorization failed. You can close this window and try again.
What it means
During the Enable Banking callback, the pending authorization promise rejected (the token exchange or provider call threw). The handler catches it, cleans up the pending auth state, and returns HTTP 500 with this HTML page. It is a generic catch-all for anything failing after a well-formed callback (bad code, provider outage, expired/unknown state session).
Source
Thrown at packages/sync-server/src/app-enablebanking/app-enablebanking.ts:163
'<script>setTimeout(function(){window.close()},1000)</script></body></html>',
);
} catch (error) {
const errorResult = {
error: error instanceof Error ? error.message : 'unknown error',
};
completedAuths.set(state, errorResult);
setTimeout(() => completedAuths.delete(state), COMPLETED_AUTH_TTL_MS);
const pending = pendingAuths.get(state);
if (pending) {
pending.reject(error);
cleanupPendingAuth(state);
}
debug('Callback auth error: %s', error);
res
.status(500)
.send(
'<html><body><p>Authorization failed. You can close this window and try again.</p></body></html>',
);
}
});
app.use(validateSessionMiddleware);
// --- Poll/complete-auth coordination ---
type PendingAuth = {
id: string;
resolve: (value: unknown) => void;
reject: (reason: unknown) => void;
timer: ReturnType<typeof setTimeout>;
};
// NOTE: These in-memory maps make the auth handoff process-local.View on GitHub (pinned to d4334cb6e6)
Solutions
- Check sync-server logs (debug output shows 'Callback auth error: %s') for the underlying cause
- Restart the bank-linking flow to get a fresh code and state
- Verify Enable Banking API credentials/app IDs are correct and the provider is reachable
- Retry after a short delay if the provider was temporarily unavailable
Example fix
null
Defensive patterns
Strategy: try-catch
Try / catch
try {
await linkBankViaEnableBanking();
} catch (err) {
logger.error('enablebanking callback failed', err);
// prompt user to restart the bank-linking flow with a fresh state
} Prevention
- Complete the bank authorization promptly to avoid expired pending auth
- Monitor sync-server logs ('Callback auth error') for root causes
- Validate Enable Banking credentials before linking
- Treat this as retryable: always restart a fresh authorization rather than reusing codes
When it happens
Trigger: The deferred promise registered for this `state` rejects — e.g. token exchange with Enable Banking fails, the code is invalid or already used, the pending auth expired, or the bank API returns an error during session creation.
Common situations: Authorization codes being replayed after a refresh; Enable Banking API outages; expired pending auth because the user took too long at the bank's login page; TLS or network errors between the sync-server and Enable Banking.
Related errors
- Authorization failed: missing code.
- Authorization failed: missing state parameter.
- NOT_CONFIGURED
- TIMED_OUT
- Failed to retrieve owner count
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/fdac31d6992c8e70.
Report an issue: GitHub.