nocobase/nocobase · error
OAuth device sign-in expired. Run `nb env auth ${options.env
Error message
OAuth device sign-in expired. Run `nb env auth ${options.envName}` to try again. What it means
Thrown by pollDeviceToken when the token endpoint returns the OAuth error code `expired_token`, meaning the device_code issued at the start of device authorization has passed its server-defined lifetime before the user completed sign-in. The CLI stops polling immediately and tells the user to restart the flow, because the device code is no longer valid.
Source
Thrown at packages/core/cli/src/lib/env-auth.ts:1106
if (response.ok) {
if (!data || typeof data !== 'object' || typeof data.access_token !== 'string') {
throw new Error('OAuth token response is missing access_token.');
}
return data as OauthTokenResponse;
}
const oauthError = typeof data === 'object' && data ? String((data as { error?: unknown }).error || '') : '';
if (oauthError === 'authorization_pending') {
updateTask(`Waiting for you to approve device sign-in for "${options.envName}"...`);
continue;
}
if (oauthError === 'slow_down') {
intervalMs += 5_000;
updateTask(`OAuth server asked us to slow down. Polling again in ${Math.ceil(intervalMs / 1000)}s...`);
continue;
}
if (oauthError === 'expired_token') {
throw new Error(`OAuth device sign-in expired. Run \`nb env auth ${options.envName}\` to try again.`);
}
if (oauthError === 'access_denied') {
throw new Error('OAuth device sign-in was denied.');
}
throw new Error(formatOauthError('Failed to poll OAuth device authorization', data, response.status));
}
throw new Error(`OAuth device sign-in timed out. Run \`nb env auth ${options.envName}\` to try again.`);
}
async function refreshOauthAccessToken(options: {
envName: string;
baseUrl: string;
auth: OauthAuthConfig;
scope?: AuthStoreOptions['scope'];
}) {
if (!options.auth.refreshToken || !options.auth.clientId) {View on GitHub (pinned to fa42722fef)
Solutions
- Run `nb env auth <envName>` again and complete the browser approval promptly.
- Open the verification_uri as soon as it is printed and approve before the code expires.
- If the server expires codes too quickly for your workflow, ask the admin to increase the device-code lifetime.
Defensive patterns
Strategy: fallback
Try / catch
try {
await cliEnvAuth(envName);
} catch (err) {
if (String(err.message).includes('device sign-in expired')) {
// restart the flow and approve promptly this time
await cliEnvAuth(envName);
} else throw err;
} Prevention
- Open the verification URL immediately when printed
- Approve before the printed code expires (usually a few minutes)
- Avoid leaving `nb env auth` idle in a terminal while doing other work
When it happens
Trigger: POST to the token endpoint with grant_type=urn:ietf:params:oauth:grant-type:device_code returns {"error":"expired_token"} — i.e. the user did not approve the sign-in in the browser before the device code expired (typically 5-15 minutes).
Common situations: Developer opened the verification URL late or left the browser tab pending; slow_down backoff stretched polling past expiry; the CLI was left running while the user was away; a server configured with a very short device-code TTL.
Related errors
- OAuth device sign-in timed out. Run `nb env auth ${options.e
- ${reason}
- OAuth server does not expose a device authorization endpoint
- Failed to start OAuth device authorization. (formatOauthFetc
- Failed to start OAuth device authorization (formatOauthError
AI-assisted analysis of nocobase/nocobase@fa42722fef (2026-09-01).
Data as JSON: /api/errors/f64047c533502c00.
Report an issue: GitHub.