{"record":{"id":"59cf98d61cc18573","repo":"gastownhall/beads","slug":"request-failed-w","errorCode":null,"errorMessage":"request failed: %w","messagePattern":"request failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/linear/client.go","lineNumber":895,"sourceCode":"\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to marshal request: %w\", err)\n\t}\n\n\thttpReq, err := http.NewRequestWithContext(ctx, \"POST\", c.Endpoint, bytes.NewReader(body))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create request: %w\", err)\n\t}\n\n\thttpReq.Header.Set(\"Content-Type\", \"application/json\")\n\tauthValue, err := c.authHeader()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\thttpReq.Header.Set(\"Authorization\", authValue)\n\n\tresp, err := c.HTTPClient.Do(httpReq)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"request failed: %w\", err)\n\t}\n\n\trespBody, err := io.ReadAll(io.LimitReader(resp.Body, MaxResponseSize))\n\t_ = resp.Body.Close()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to read response: %w\", err)\n\t}\n\n\tif resp.StatusCode < 200 || resp.StatusCode >= 300 {\n\t\treturn nil, fmt.Errorf(\"API error: %s (status %d)\", string(respBody), resp.StatusCode)\n\t}\n\n\tvar gqlResp struct {\n\t\tData   json.RawMessage `json:\"data\"`\n\t\tErrors []GraphQLError  `json:\"errors,omitempty\"`\n\t}\n\tif err := json.Unmarshal(respBody, &gqlResp); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse response: %w (body: %s)\", err, string(respBody))","sourceCodeStart":877,"sourceCodeEnd":913,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/linear/client.go#L877-L913","documentation":"This error wraps a failure of c.HTTPClient.Do(httpReq) — the HTTP request to Linear's API never completed with a response. Typical causes are DNS failures, connection refused/timeouts, TLS problems, or the context being canceled mid-flight. It is a transport-level failure, distinct from API errors returned with an HTTP status.","triggerScenarios":"Network outage or DNS failure resolving api.linear.app; connection timeout because HTTPClient has no/short Timeout and Linear is slow or unreachable; ctx canceled while the request is in flight; TLS certificate errors (corporate proxy, wrong system clock); invalid proxy settings in the environment.","commonSituations":"CI runners without network egress; corporate proxy/VPN blocking api.linear.app; HTTP_CLIENT_TIMEOUT configured too aggressively; container DNS misconfiguration; server shutting down and canceling the context.","solutions":["Check network reachability: curl https://api.linear.app/graphql from the same host","Inspect the wrapped cause (net.Error, context.DeadlineExceeded, x509 errors) via errors.As and handle each class","Increase the HTTP client timeout or retry on transient network errors with backoff","Verify proxy environment variables (HTTP_PROXY/HTTPS_PROXY) and TLS trust store are correct"],"exampleFix":"// before\nclient := &http.Client{} // no timeout; hangs then fails on dead network\n// after\nclient := &http.Client{Timeout: 30 * time.Second}\nif err != nil {\n    var ne net.Error\n    if errors.As(err, &ne) && ne.Timeout() {\n        // retry with backoff\n    }\n}","handlingStrategy":"retry","validationCode":"// preflight connectivity check\nresp, err := http.Head(\"https://api.linear.app\")\nif err != nil {\n    return fmt.Errorf(\"linear unreachable: %w\", err)\n}\nresp.Body.Close()","typeGuard":null,"tryCatchPattern":"var ne net.Error\nif errors.As(err, &ne) && ne.Timeout() {\n    // retry with backoff\n}\nif errors.Is(err, context.Canceled) {\n    // caller canceled; do not retry\n}","preventionTips":["Set a sane HTTP client timeout (e.g. 30s) on the Linear client","Implement exponential backoff retry for transient transport errors","Check proxy/VPN/DNS health in deployment environments","Distinguish context cancellation from real network failures before retrying"],"tags":["network","http-client","timeout"],"backgroundTag":"http-request-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}