actualbudget/actual · warning
No token received.
Error message
No token received.
What it means
In the desktop (Electron) app's OAuth callback, an inline HTTP server listens on 127.0.0.1 for the provider's redirect. If the request arrives without the expected token query parameter (the handler closes the server when a token is received, otherwise it responds), the server replies 400 with the plain-text body 'No token received.' and the OAuth flow fails.
Source
Thrown at packages/desktop-electron/index.ts:130
);
} else {
void clientWin.loadURL(`app://actual/openid-cb?token=${code}`);
}
// Respond to the browser
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('OpenID login successful! You can close this tab.');
// Clean up the server after receiving the code. Wait for the listener
// to fully release port 3010 before clearing the reference, otherwise a
// subsequent start-oauth-server request could try to bind the port
// while this listener is still shutting down.
await new Promise<void>(closeResolve => {
server.close(() => closeResolve());
});
oAuthServer = null;
} else {
res.writeHead(400, { 'Content-Type': 'text/plain' });
res.end('No token received.');
}
});
server.listen(port, '127.0.0.1', () => {
logMessage('info', `OAuth server started on port: ${port}`);
resolve({ url: `http://localhost:${port}`, server });
});
});
};
if (isDev) {
process.traceProcessWarnings = true;
}
async function loadGlobalPrefs() {
let state: GlobalPrefsJson = {};
try {View on GitHub (pinned to d4334cb6e6)
Solutions
- Retry the sign-in flow and complete authorization at the provider instead of cancelling.
- Check the provider's redirect for an error query parameter (access_denied etc.) and address the underlying cause (app not approved, account blocked).
- Verify the OAuth redirect URI / port configured in the app matches what the provider is allowed to redirect to.
- Check for browser extensions or proxies that alter the redirect URL and drop the token parameter.
Example fix
// before: provider redirect missing token http://127.0.0.1:3999/oauth?state=xyz // after: redirect must include the token http://127.0.0.1:3999/oauth?token=abc123&state=xyz
Defensive patterns
Strategy: validation
Validate before calling
const parsed = new URL(redirectUrl, 'http://127.0.0.1');
if (!parsed.searchParams.get('token')) {
const err = parsed.searchParams.get('error');
throw new Error(`OAuth redirect missing token${err ? ` (provider error: ${err})` : ''}`);
} Type guard
function hasOAuthToken(u: URL): boolean {
return typeof u.searchParams.get('token') === 'string' && u.searchParams.get('token')!.length > 0;
} Try / catch
try {
const token = await startOAuthFlow(); // resolves with token from local callback server
} catch (e) {
if (e.message.includes('No token received')) {
logger.warn('OAuth login cancelled or redirect lacked a token; prompting user to retry');
}
} Prevention
- Complete the authorization flow instead of cancelling at the consent screen.
- Verify the OAuth redirect port/URI matches the app's local listener configuration.
- Check provider error query params (error=access_denied) in callback handling and surface them to the user.
- Test the flow with browser extensions/proxies disabled to rule out URL tampering.
When it happens
Trigger: The OAuth identity provider redirects back to http://127.0.0.1:<port> without a token (or code-derived token) query parameter — e.g. the user cancels login at the provider, the provider sends an error response, or the redirect URL was modified/corrupted so the token param is missing.
Common situations: User cancels the OAuth consent screen; provider redirects with error=access_denied instead of a token; a mismatch between the configured redirect port and the app's listening port causing a malformed/foreign request to hit the callback; browser extensions stripping query params.
Related errors
- User not found
- Authorization failed: missing code.
- Authorization failed: missing state parameter.
- invalid-password
- applyAppUpdate not implemented in electron app
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/a309f5c6950a07c1.
Report an issue: GitHub.