{"record":{"id":"40322c584972afb8","repo":"router-for-me/CLIProxyAPI","slug":"xai-device-code-expired","errorCode":null,"errorMessage":"xai device code expired","messagePattern":"xai device code expired","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/auth/xai/xai.go","lineNumber":238,"sourceCode":"\tif deviceCode.ExpiresIn > 0 {\n\t\tcodeDeadline := time.Now().Add(time.Duration(deviceCode.ExpiresIn) * time.Second)\n\t\tif codeDeadline.Before(deadline) {\n\t\t\tdeadline = codeDeadline\n\t\t}\n\t}\n\n\t// Poll immediately once, then wait between subsequent attempts.\n\tfirstAttempt := true\n\ttimer := time.NewTimer(0)\n\tdefer timer.Stop()\n\n\tfor {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn nil, fmt.Errorf(\"xai device code: context cancelled: %w\", ctx.Err())\n\t\tcase <-timer.C:\n\t\t\tif !firstAttempt && time.Now().After(deadline) {\n\t\t\t\treturn nil, fmt.Errorf(\"xai device code expired\")\n\t\t\t}\n\t\t\tfirstAttempt = false\n\n\t\t\ttoken, pollErr, nextInterval, shouldContinue := a.exchangeDeviceCode(ctx, tokenEndpoint, deviceCode.DeviceCode, interval)\n\t\t\tif token != nil {\n\t\t\t\treturn token, nil\n\t\t\t}\n\t\t\tif !shouldContinue {\n\t\t\t\treturn nil, pollErr\n\t\t\t}\n\t\t\tinterval = nextInterval\n\t\t\ttimer.Reset(interval)\n\t\t}\n\t}\n}\n\n// exchangeDeviceCode attempts to exchange a device code for tokens.\n// Returns (token, error, nextInterval, shouldContinue).","sourceCodeStart":220,"sourceCodeEnd":256,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/auth/xai/xai.go#L220-L256","documentation":"The local deadline computed for the device code (from its expires_in, with fallbacks) has passed and at least one poll attempt was already made, so PollForToken stops polling. This is the client-side expiry of the OAuth device code: the user never completed authorization in time.","triggerScenarios":"User does not visit the verification URI and enter the user_code before the code's expiry; authorization completes but the user takes longer than expires_in.","commonSituations":"Unattended/headless machine where the browser never opens; user steps away during the interactive step; expiry misparsed (e.g. expires_in in ms vs s) making the deadline too short.","solutions":["Restart the flow: request a fresh device code and complete authorization promptly","Use verification_uri_complete (pre-filled code) or auto-open the browser to reduce user delay","Verify the expires_in units and clock skew if codes seem to expire instantly"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"tokenData, err := auth.WaitForAuthorization(ctx, deviceCode)\nif err != nil && strings.Contains(err.Error(), \"device code expired\") {\n    log.Info(\"device code expired; requesting a new one\")\n    deviceCode, err = auth.RequestDeviceCode(ctx)\n    if err != nil { return err }\n    tokenData, err = auth.WaitForAuthorization(ctx, deviceCode)\n}","preventionTips":["Open the verification URL immediately after issuance (auto-open browser or print URI)","Use verification_uri_complete so the code is pre-filled","Bound total wait time to the code lifetime and restart cleanly on expiry"],"tags":["oauth","device-flow","xai","expiry"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}