{"record":{"id":"0f0886919a4d28d9","repo":"sipeed/picoclaw","slug":"parsing-device-code-response-w","errorCode":null,"errorMessage":"parsing device code response: %w","messagePattern":"parsing device code response: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/auth/oauth.go","lineNumber":276,"sourceCode":"\t\t\"application/json\",\n\t\tstrings.NewReader(string(reqBody)),\n\t)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"requesting device code: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"reading device code response: %w\", err)\n\t}\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"device code request failed: %s\", string(body))\n\t}\n\n\tdeviceResp, err := parseDeviceCodeResponse(body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"parsing device code response: %w\", err)\n\t}\n\n\tif deviceResp.Interval < 1 {\n\t\tdeviceResp.Interval = 5\n\t}\n\n\treturn &DeviceCodeInfo{\n\t\tDeviceAuthID: deviceResp.DeviceAuthID,\n\t\tUserCode:     deviceResp.UserCode,\n\t\tVerifyURL:    cfg.Issuer + \"/codex/device\",\n\t\tInterval:     deviceResp.Interval,\n\t}, nil\n}\n\n// PollDeviceCodeOnce makes a single poll attempt to check if the user has authenticated.\n// Returns (credential, nil) on success, (nil, nil) if still pending, or (nil, err) on failure.\nfunc PollDeviceCodeOnce(cfg OAuthProviderConfig, deviceAuthID, userCode string) (*AuthCredential, error) {\n\treturn pollDeviceCode(cfg, deviceAuthID, userCode)","sourceCodeStart":258,"sourceCodeEnd":294,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/auth/oauth.go#L258-L294","documentation":"Thrown by RequestDeviceCode (pkg/auth/oauth.go:276) when the POST to {Issuer}/api/accounts/deviceauth/usercode returned HTTP 200 but parseDeviceCodeResponse could not decode the body. The parser expects JSON with device_auth_id, user_code and an interval that is a number, numeric string, or null; any JSON decode failure or a bad interval type propagates here.","triggerScenarios":"Calling RequestDeviceCode(cfg) where the issuer endpoint replies 200 with: malformed JSON, HTML instead of JSON, missing/mis-typed fields (e.g. interval as 5.5, true, or an object), or an unexpected API envelope that fails json.Unmarshal/parseFlexibleInt.","commonSituations":"Issuer base URL misconfigured to a host that serves a 200 status page; a corporate proxy or captive portal injecting HTML; the provider renaming fields or shipping a new device-auth API version; middleware returning an empty 200 body.","solutions":["Curl the exact endpoint to inspect the payload: curl -sS -X POST -H 'Content-Type: application/json' -d '{\"client_id\":\"...\"}' $ISSUER/api/accounts/deviceauth/usercode","Verify cfg.Issuer is the OAuth issuer root (e.g. https://auth.openai.com) with no trailing slash, path, or /v1 suffix","Check the interval field type in the response: must be a number, numeric string, or null (not float/bool/object)","If the provider API changed shape, update parseDeviceCodeResponse field tags or the endpoint path in pkg/auth/oauth.go","Log the raw body next to the parse error so future failures show what the server actually returned"],"exampleFix":"// before\ndeviceResp, err := parseDeviceCodeResponse(body)\nif err != nil {\n\treturn nil, fmt.Errorf(\"parsing device code response: %w\", err)\n}\n\n// after (include raw body for diagnosis, truncated)\ndeviceResp, err := parseDeviceCodeResponse(body)\nif err != nil {\n\tsnippet := body\n\tif len(snippet) > 512 {\n\t\tsnippet = snippet[:512]\n\t}\n\treturn nil, fmt.Errorf(\"parsing device code response: %w (body: %s)\", err, snippet)\n}","handlingStrategy":"try-catch","validationCode":"// Validate issuer shape before the call\nif u, err := url.Parse(cfg.Issuer); err != nil || u.Scheme == \"\" || u.Host == \"\" {\n\treturn fmt.Errorf(\"invalid issuer URL: %q\", cfg.Issuer)\n}","typeGuard":"func isDeviceCodeParseError(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \"parsing device code response\")\n}","tryCatchPattern":"info, err := auth.RequestDeviceCode(cfg)\nif err != nil {\n\tif isDeviceCodeParseError(err) {\n\t\t// log issuer and endpoint; likely schema/issuer mismatch — do not blind-retry\n\t\tlog.Printf(\"device code schema mismatch from %s: %v\", cfg.Issuer, err)\n\t}\n\treturn err\n}","preventionTips":["Pin the expected issuer root in config validation","Add a contract test that fixtures match parseDeviceCodeResponse's expected JSON keys","Include the response body in parse-failure logs","Fail config load when Issuer lacks scheme/host instead of letting HTTP/JSON errors surface later"],"tags":["oauth","device-code","json","parsing","go"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}