{"record":{"id":"0d2a41da1ee124c4","repo":"decolua/9router","slug":"polldevicetoken-missing-nonce-or-code-verifier","errorCode":null,"errorMessage":"pollDeviceToken: missing nonce or code verifier","messagePattern":"pollDeviceToken: missing nonce or code verifier","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/lib/oauth/services/qoder.js","lineNumber":99,"sourceCode":"    return {\n      verificationUriComplete: `${QODER_LOGIN_URL}?${params.toString()}`,\n      codeVerifier: verifier,\n      nonce,\n      machineId,\n    };\n  }\n\n  /**\n   * Single poll attempt. Returns one of:\n   *   { status: \"pending\" }       — keep polling\n   *   { status: \"ok\", token, ... } — user authorized, tokens captured\n   *   throws Error                 — terminal failure\n   *\n   * Upstream returns 202/404 while waiting; 200 with a JSON body when done.\n   */\n  async pollDeviceToken({ nonce, codeVerifier }) {\n    if (!nonce || !codeVerifier) {\n      throw new Error(\"pollDeviceToken: missing nonce or code verifier\");\n    }\n    const url = `${QODER_DEVICE_TOKEN_URL}?nonce=${encodeURIComponent(nonce)}&verifier=${encodeURIComponent(codeVerifier)}&challenge_method=S256`;\n\n    const response = await fetchWithTimeout(url, {\n      method: \"GET\",\n      headers: {\n        Accept: \"application/json\",\n        \"User-Agent\": \"Go-http-client/2.0\",\n      },\n    });\n\n    // Pending — server has registered the device code but the user hasn't\n    // finished the browser flow yet. Both 202 and 404 mean \"keep polling\".\n    if (response.status === 202 || response.status === 404) {\n      return { status: \"pending\" };\n    }\n\n    const text = await response.text();","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/lib/oauth/services/qoder.js#L81-L117","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nawait qoder.pollDeviceToken({ nonce: state.device_code, codeVerifier: state.verifier });\n// after\nawait qoder.pollDeviceToken({ nonce: state.nonce, codeVerifier: state.codeVerifier });","handlingStrategy":"validation","validationCode":"function canPoll(state) {\n  return typeof state?.nonce === 'string' && state.nonce.length > 0 &&\n         typeof state?.codeVerifier === 'string' && state.codeVerifier.length > 0;\n}\nif (!canPoll(deviceFlowState)) throw new Error('device flow state incomplete: restart flow');","typeGuard":"function hasDeviceFlowState(s) {\n  return s !== null && typeof s === 'object' &&\n    typeof s.nonce === 'string' && s.nonce.length > 0 &&\n    typeof s.codeVerifier === 'string' && s.codeVerifier.length > 0;\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["oauth","device-flow","validation","pkce"],"backgroundTag":"missing-oauth-params","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}