decolua/9router · error
pollDeviceToken: missing nonce or code verifier
Error message
pollDeviceToken: missing nonce or code verifier
What it means
QoderService.pollDeviceToken polls Qoder's device-token endpoint using the nonce and PKCE code verifier issued at flow start. The method throws this terminal error immediately when either argument is missing or empty, before any network call is made. It is a programmer-error guard: the OAuth device flow cannot complete without both values.
Source
Thrown at src/lib/oauth/services/qoder.js:99
return {
verificationUriComplete: `${QODER_LOGIN_URL}?${params.toString()}`,
codeVerifier: verifier,
nonce,
machineId,
};
}
/**
* Single poll attempt. Returns one of:
* { status: "pending" } — keep polling
* { status: "ok", token, ... } — user authorized, tokens captured
* throws Error — terminal failure
*
* Upstream returns 202/404 while waiting; 200 with a JSON body when done.
*/
async pollDeviceToken({ nonce, codeVerifier }) {
if (!nonce || !codeVerifier) {
throw new Error("pollDeviceToken: missing nonce or code verifier");
}
const url = `${QODER_DEVICE_TOKEN_URL}?nonce=${encodeURIComponent(nonce)}&verifier=${encodeURIComponent(codeVerifier)}&challenge_method=S256`;
const response = await fetchWithTimeout(url, {
method: "GET",
headers: {
Accept: "application/json",
"User-Agent": "Go-http-client/2.0",
},
});
// Pending — server has registered the device code but the user hasn't
// finished the browser flow yet. Both 202 and 404 mean "keep polling".
if (response.status === 202 || response.status === 404) {
return { status: "pending" };
}
const text = await response.text();View on GitHub (pinned to 90b52e06ff)
Solutions
- Ensure startDeviceFlow's return value (containing nonce and codeVerifier) is captured and persisted before polling.
- Pass fields exactly as { nonce, codeVerifier } — the method does not accept snake_case aliases.
- Before polling, check both values are non-empty strings and abort/restart the device flow if the state was lost.
Example fix
// before
await qoder.pollDeviceToken({ nonce: state.device_code, codeVerifier: state.verifier });
// after
await qoder.pollDeviceToken({ nonce: state.nonce, codeVerifier: state.codeVerifier }); Defensive patterns
Strategy: validation
Validate before calling
function canPoll(state) {
return typeof state?.nonce === 'string' && state.nonce.length > 0 &&
typeof state?.codeVerifier === 'string' && state.codeVerifier.length > 0;
}
if (!canPoll(deviceFlowState)) throw new Error('device flow state incomplete: restart flow'); Type guard
function hasDeviceFlowState(s) {
return s !== null && typeof s === 'object' &&
typeof s.nonce === 'string' && s.nonce.length > 0 &&
typeof s.codeVerifier === 'string' && s.codeVerifier.length > 0;
} Prevention
- Persist startDeviceFlow's full return object verbatim; never reconstruct it field-by-field.
- Use a single state object keyed by the exact property names (nonce, codeVerifier).
- Check state completeness before entering the polling loop.
When it happens
Trigger: Calling pollDeviceToken({}) or with null/undefined/empty-string nonce or codeVerifier — e.g. when startDeviceFlow's returned state was dropped, or fields were read under wrong key names (code_verifier vs codeVerifier).
Common situations: Storing device-flow state in a DB/session and deserializing with wrong property names; passing the device code instead of the nonce; losing the verifier across a server restart so the PKCE check can never succeed.
Related errors
- Missing Zed callback URL
- Invalid Zed callback URL
- Zed callback must include user_id and access_token
- Missing xAI authorization code
- Missing accessToken
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/0d2a41da1ee124c4.
Report an issue: GitHub.