{"record":{"id":"d47275c2f3cd5a3c","repo":"sipeed/picoclaw","slug":"invalid-integer-value-s","errorCode":null,"errorMessage":"invalid integer value: %s","messagePattern":"invalid integer value: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/auth/oauth.go","lineNumber":339,"sourceCode":"\tif len(raw) == 0 || string(raw) == \"null\" {\n\t\treturn 0, nil\n\t}\n\n\tvar interval int\n\tif err := json.Unmarshal(raw, &interval); err == nil {\n\t\treturn interval, nil\n\t}\n\n\tvar intervalStr string\n\tif err := json.Unmarshal(raw, &intervalStr); err == nil {\n\t\tintervalStr = strings.TrimSpace(intervalStr)\n\t\tif intervalStr == \"\" {\n\t\t\treturn 0, nil\n\t\t}\n\t\treturn strconv.Atoi(intervalStr)\n\t}\n\n\treturn 0, fmt.Errorf(\"invalid integer value: %s\", string(raw))\n}\n\nfunc LoginDeviceCode(cfg OAuthProviderConfig) (*AuthCredential, error) {\n\treqBody, _ := json.Marshal(map[string]string{\n\t\t\"client_id\": cfg.ClientID,\n\t})\n\n\tresp, err := http.Post(\n\t\tcfg.Issuer+\"/api/accounts/deviceauth/usercode\",\n\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)","sourceCodeStart":321,"sourceCodeEnd":357,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/auth/oauth.go#L321-L357","documentation":"Produced by parseFlexibleInt (pkg/auth/oauth.go:339) while decoding the interval field of the device-code response. The code first tries json.Unmarshal into int, then into string (accepting numeric strings); if both fail it rejects the value with 'invalid integer value: %s'. This means the JSON value is neither an integer nor a string — it is a float (e.g. 5.5), boolean, object, or array.","triggerScenarios":"The deviceauth/usercode response contains \"interval\": 5.5, \"interval\": true, \"interval\": {}, or \"interval\": []. Note: a non-numeric string like \"abc\" instead surfaces a strconv.Atoi error, and null/missing/empty-string interval are accepted as 0.","commonSituations":"Provider starts returning a fractional polling interval or switches interval to an object like {\"seconds\":5}; an API gateway rewriting the payload; a mocked/stubbed test server emitting JSON typed differently from production.","solutions":["Inspect the live response and confirm the JSON type of interval","Fix the server/test fixture to emit an integer (\"interval\": 5) or a numeric string (\"interval\": \"5\")","If fractional intervals are legitimate, extend parseFlexibleInt to unmarshal into float64 and truncate/round to int seconds"],"exampleFix":"// before (in parseFlexibleInt)\nreturn 0, fmt.Errorf(\"invalid integer value: %s\", string(raw))\n\n// after (accept fractional seconds)\nvar intervalFloat float64\nif err := json.Unmarshal(raw, &intervalFloat); err == nil {\n\treturn int(intervalFloat), nil\n}\nreturn 0, fmt.Errorf(\"invalid integer value: %s\", string(raw))","handlingStrategy":"validation","validationCode":"// Pre-check the interval field's JSON type before relying on the parse\nvar probe struct {\n\tInterval json.RawMessage `json:\"interval\"`\n}\nif err := json.Unmarshal(body, &probe); err == nil {\n\tif len(probe.Interval) > 0 {\n\t\tvar f float64\n\t\tvar s string\n\t\tif json.Unmarshal(probe.Interval, &f) == nil || json.Unmarshal(probe.Interval, &s) == nil {\n\t\t\t// number or string: parseFlexibleInt will accept it\n\t\t}\n\t}\n}","typeGuard":"func isInvalidIntervalError(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \"invalid integer value\")\n}","tryCatchPattern":"if _, err := auth.RequestDeviceCode(cfg); err != nil {\n\tif isInvalidIntervalError(err) {\n\t\t// server contract changed: interval is neither number nor string\n\t\treturn fmt.Errorf(\"provider interval field has unsupported type: %w\", err)\n\t}\n\treturn err\n}","preventionTips":["Contract-test the provider's interval field type on every provider API version bump","Keep mock servers byte-compatible with production responses","Prefer integer intervals when you control the server","Extend parseFlexibleInt for float intervals if the provider legitimately sends them"],"tags":["json","parsing","device-code","go","type-mismatch"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}