{"record":{"id":"083518182a9f4145","repo":"jlcodes99/cockpit-tools","slug":"qoder-oauth","errorCode":null,"errorMessage":"Qoder OAuth 授权链接为空","messagePattern":"Qoder OAuth 授权链接为空","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/pages/QoderAccountsPage.tsx","lineNumber":1256,"sourceCode":"          loginId: response.loginId,\n          attemptSeq,\n        });\n        return;\n      }\n\n      const loginId = response.loginId;\n      const verificationUri =\n        response.verificationUri ||\n        (response as unknown as { verification_uri?: string }).verification_uri ||\n        '';\n      if (!verificationUri) {\n        const responseKeys = Object.keys(response);\n        logQoderOauthUi('prepare:verification-uri-empty', {\n          loginId,\n          responseKeyCount: responseKeys.length,\n          responseKeys: responseKeys.slice(0, 12),\n        });\n        throw new Error('Qoder OAuth 授权链接为空');\n      }\n      logQoderOauthUi('prepare:will-set-state', {\n        loginId,\n        verificationUriLength: verificationUri.length,\n        callbackUrl: response.callbackUrl ?? null,\n        currentOauthSessionRef: oauthSessionRef.current,\n      });\n      oauthSessionRef.current = loginId;\n      setOauthLoginId(loginId);\n      setOauthUrl(verificationUri);\n      setOauthPreparing(false);\n      logQoderOauthUi('prepare:state-set-done', { loginId });\n      startCompletePolling(loginId);\n    } catch (error) {\n      if (attemptSeq !== oauthAttemptSeqRef.current) return;\n      logQoderOauthUi('prepare:start-failed', { error: String(error) });\n      const msg = String(error);\n      setOauthPreparing(false);","sourceCodeStart":1238,"sourceCodeEnd":1274,"githubUrl":"https://github.com/jlcodes99/cockpit-tools/blob/1ed8b77992d62ca81fabf744deb0839ad361d5bf/src/pages/QoderAccountsPage.tsx#L1238-L1274","documentation":"During the Qoder OAuth device-flow preparation, after requesting a device code the page expects a verification URI. If verificationUri is empty, it logs the raw response keys ('prepare:verification-uri-empty') and throws a plain Error('Qoder OAuth 授权链接为空' — Qoder OAuth authorization link is empty). This signals the provider returned a response without the required verification_uri field.","triggerScenarios":"The Qoder device-authorization endpoint responds with a 200 body that lacks verificationUri (or it is an empty string), so the check before entering the waiting/polling state throws.","commonSituations":"Qoder backend changes or partial outages returning malformed device-code responses; proxy/gateway stripping fields; API version drift between client and service; network middleware returning an unexpected success body.","solutions":["Retry the OAuth preparation — a transient backend issue may return an incomplete response.","Inspect the logged 'prepare:verification-uri-empty' entry (responseKeys) to see what the server actually returned.","Check Qoder service status / API version compatibility if responses are consistently missing the field.","Validate the response shape before use and surface a retry-able error to the user."],"exampleFix":"// before\nif (!verificationUri) {\n  throw new Error('Qoder OAuth 授权链接为空');\n}\n// after\nif (!verificationUri) {\n  logQoderOauthUi('prepare:verification-uri-empty', { loginId });\n  throw new RetryableError('Qoder OAuth 授权链接为空'); // caller retries prepare\n}","handlingStrategy":"try-catch","validationCode":"if (typeof response.verificationUri !== 'string' || response.verificationUri.length === 0) {\n  // abort and retry prepare\n}","typeGuard":"const hasVerificationUri = (r: unknown): r is { verificationUri: string } & Record<string, unknown> =>\n  typeof r === 'object' && r !== null &&\n  typeof (r as any).verificationUri === 'string' &&\n  (r as any).verificationUri.length > 0;","tryCatchPattern":"try {\n  await startQoderOauthPrepare();\n} catch (e) {\n  if (e.message === 'Qoder OAuth 授权链接为空') {\n    showRetryDialog(t('qoder.oauth.retryPrepare'));\n    return;\n  }\n  throw e;\n}","preventionTips":["Validate the device-authorization response shape before consuming it.","Implement automatic retry with backoff for the prepare step.","Watch the 'prepare:verification-uri-empty' logs to catch provider-side regressions early."],"tags":["oauth","device-flow","network"],"backgroundTag":"oauth-verification-uri-missing","analyzedSha":"1ed8b77992d62ca81fabf744deb0839ad361d5bf","analyzedAt":"2026-09-05T09:51:41.178Z","contentChangedAt":"2026-09-05T09:51:41.178Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}