{"record":{"id":"e1c09798a4a22266","repo":"larksuite/cli","slug":"device-authorization-failed-http-d-response-no","errorCode":null,"errorMessage":"Device authorization failed: HTTP %d – response not JSON","messagePattern":"Device authorization failed: HTTP (.+?) – response not JSON","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/auth/device_flow.go","lineNumber":107,"sourceCode":"\t}\n\treq.Header.Set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\treq.Header.Set(\"Authorization\", \"Basic \"+basicAuth)\n\n\tresp, err := httpClient.Do(req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer resp.Body.Close()\n\tlogHTTPResponse(resp)\n\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"Device authorization failed: read body: %v\", err)\n\t}\n\n\tvar data map[string]interface{}\n\tif err := json.Unmarshal(body, &data); err != nil {\n\t\treturn nil, fmt.Errorf(\"Device authorization failed: HTTP %d – response not JSON\", resp.StatusCode)\n\t}\n\n\t_, hasError := data[\"error\"]\n\tif resp.StatusCode >= 400 || hasError {\n\t\tmsg := getStr(data, \"error_description\")\n\t\tif msg == \"\" {\n\t\t\tmsg = getStr(data, \"error\")\n\t\t}\n\t\tif msg == \"\" {\n\t\t\tmsg = \"Unknown error\"\n\t\t}\n\t\treturn nil, fmt.Errorf(\"Device authorization failed: %s\", msg)\n\t}\n\n\texpiresIn := getInt(data, \"expires_in\", 240)\n\tinterval := getInt(data, \"interval\", 5)\n\n\tverificationUri := getStr(data, \"verification_uri\")","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/auth/device_flow.go#L89-L125","documentation":"RequestDeviceAuthorization parsed the device-authorization endpoint's body as JSON and json.Unmarshal failed. Either the server returned non-JSON (HTML error page, empty body, gateway message) or the body was truncated. The HTTP status is embedded in the message to hint at the source.","triggerScenarios":"json.Unmarshal(body, &data) returns an error in RequestDeviceAuthorization — e.g. an LB/gateway returns an HTML 502 page, an empty 200 body, or the endpoint is blocked and a captive portal responds.","commonSituations":"Corporate proxy returning an HTML block page for open.feishu.cn; wrong base URL/endpoint override pointing at a non-Lark server; gateway outage serving plain-text error pages.","solutions":["Check the HTTP status in the message: 4xx/5xx from a gateway usually means the request never reached the auth service — verify network/proxy settings.","Verify the base URL / endpoint configuration points at open.feishu.cn.","Log or dump the raw body (before unmarshal) to see what was actually returned.","Retry after confirming connectivity; a transient gateway page will disappear on retry."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// preflight: verify the device-auth endpoint returns JSON before starting the flow\nresp, _ := http.Get(deviceAuthURL)\nb, _ := io.ReadAll(io.LimitReader(resp.Body, 512))\nif !json.Valid(b) && resp.StatusCode != http.StatusOK {\n    log.Fatalf(\"endpoint not returning JSON (HTTP %d): %.200s\", resp.StatusCode, b)\n}","typeGuard":"func isNonJSONDeviceAuthErr(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"response not JSON\")\n}","tryCatchPattern":"resp, err := auth.RequestDeviceAuthorization(ctx, clientID, scopes)\nif isNonJSONDeviceAuthErr(err) {\n    return fmt.Errorf(\"gateway/proxy returned non-JSON; check network and base URL: %w\", err)\n}","preventionTips":["Verify base URL and endpoint overrides point at open.feishu.cn.","Test from inside the target network to catch proxy block pages early.","Check the HTTP status embedded in the message to distinguish gateway vs API errors.","Log raw response bodies when debugging auth flows."],"tags":["json","http","device-flow","parse-error"],"backgroundTag":"non-json-response","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}