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

  1. Check sync-server logs (debug output shows 'Callback auth error: %s') for the underlying cause
  2. Restart the bank-linking flow to get a fresh code and state
  3. Verify Enable Banking API credentials/app IDs are correct and the provider is reachable
  4. 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

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


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