{"record":{"id":"8399d7f6c04579fa","repo":"larksuite/cli","slug":"device-authorization-failed-read-body-v","errorCode":null,"errorMessage":"Device authorization failed: read body: %v","messagePattern":"Device authorization failed: read body: (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/auth/device_flow.go","lineNumber":102,"sourceCode":"\tform.Set(\"scope\", scope)\n\n\treq, err := http.NewRequest(\"POST\", endpoints.DeviceAuthorization, strings.NewReader(form.Encode()))\n\tif err != nil {\n\t\treturn nil, err\n\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}","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/auth/device_flow.go#L84-L120","documentation":"RequestDeviceAuthorization wraps an io.ReadAll failure while draining the device-authorization endpoint's HTTP response body. The HTTP request itself completed, but the response bytes could not be read (connection reset mid-body, timeout, truncated response). The %v verb embeds the underlying reader error without %w wrapping, so errors.Is/As cannot unwrap the cause.","triggerScenarios":"RequestDeviceAuthorization receives a 2xx/other response whose body read via io.ReadAll fails — server closes the connection mid-response, proxy truncates the transfer, or a network interruption occurs while streaming the body.","commonSituations":"Flaky corporate proxies or VPNs dropping keep-alive connections mid-body; Lark gateway timeouts on slow links; running the device flow behind an intercepting firewall that cuts large responses.","solutions":["Retry RequestDeviceAuthorization — this is a transient transport-level read failure, not an auth rejection.","Check network path to open.feishu.cn (proxy/VPN/firewall) and retry from a stable connection.","If it recurs, capture the embedded error text after 'read body:' to identify the transport cause (reset, timeout, unexpected EOF).","Consider replacing %v with %w when editing the code so callers can unwrap the cause."],"exampleFix":"// before\nreturn nil, fmt.Errorf(\"Device authorization failed: read body: %v\", err)\n// after\nreturn nil, fmt.Errorf(\"Device authorization failed: read body: %w\", err)","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// retry up to 3 times on transient body-read failure\nfor i := 0; i < 3; i++ {\n    resp, err := auth.RequestDeviceAuthorization(ctx, clientID, scopes)\n    if err == nil { break }\n    if strings.Contains(err.Error(), \"read body:\") && i < 2 { time.Sleep(backoff); continue }\n    return err\n}","preventionTips":["Run the device flow on a stable network; avoid flaky VPN/proxy paths.","Use a context with a generous timeout so partial reads fail fast and retry cleanly.","When touching the code, wrap with %w so callers can errors.Is on transport errors.","Log the underlying cause text for support diagnostics."],"tags":["network","http","device-flow","go"],"backgroundTag":"http-response-read-failed","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"}