{"record":{"id":"785f1f7da9f5a123","repo":"chenhg5/cc-connect","slug":"webex-downloadfile-status-d","errorCode":null,"errorMessage":"webex: downloadFile status %d","messagePattern":"webex: downloadFile status (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/webex/client.go","lineNumber":190,"sourceCode":"\tdefer func() { _ = resp.Body.Close() }()\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"webex: getMessage status %d\", resp.StatusCode)\n\t}\n\tvar m message\n\tif err := json.NewDecoder(resp.Body).Decode(&m); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &m, nil\n}\n\nfunc (c *httpClient) DownloadFile(ctx context.Context, url string) (*downloadedFile, error) {\n\tresp, err := c.doWithRetry(ctx, http.MethodGet, url, nil, \"\", \"webex: downloadFile\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"webex: downloadFile status %d\", resp.StatusCode)\n\t}\n\tdata, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tf := &downloadedFile{Data: data, MimeType: resp.Header.Get(\"Content-Type\")}\n\tif cd := resp.Header.Get(\"Content-Disposition\"); cd != \"\" {\n\t\tif _, params, err := mime.ParseMediaType(cd); err == nil {\n\t\t\tf.FileName = params[\"filename\"]\n\t\t}\n\t}\n\treturn f, nil\n}\n\nfunc (c *httpClient) PostMessage(ctx context.Context, roomID, parentID, markdown string) error {\n\tbody := map[string]string{\"roomId\": roomID, \"markdown\": markdown}\n\tif parentID != \"\" {\n\t\tbody[\"parentId\"] = parentID","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/webex/client.go#L172-L208","documentation":"DownloadFile fetches an attachment over the Webex REST API. After retries, any HTTP status other than 200 aborts the download and this error wraps the status code. It means the request reached Webex but the response was not a successful download.","triggerScenarios":"Calling DownloadFile for an attachment URL that returns 401 (expired/invalid token), 404 (deleted or expired attachment), or 403/5xx from the Webex content API.","commonSituations":"Bots trying to re-download old attachments whose content URLs expired; token revoked or lacking scope; transient Webex CDN errors surfacing as 502/503 after retries.","solutions":["Log the status code to identify the cause: 401/403 → fix the bot token, 404 → the attachment is gone","Check the bot token validity and scopes; regenerate if expired or revoked","Add caller-side handling: for 404, treat as non-retryable and skip the file; for 5xx, retry later","Verify the URL passed to DownloadFile came directly from the message payload, not a stale copy"],"exampleFix":"// before\nf, err := client.DownloadFile(ctx, staleURL)\n// after\nf, err := client.DownloadFile(ctx, msg.Files[0])\nif err != nil {\n    var httpErr interface{ Error() string }\n    if strings.Contains(err.Error(), \"status 404\") {\n        return nil // attachment expired, skip\n    }\n    return err\n}","handlingStrategy":"try-catch","validationCode":"if u == \"\" || !strings.HasPrefix(u, \"http\") { skip download }","typeGuard":null,"tryCatchPattern":"f, err := client.DownloadFile(ctx, url)\nif err != nil {\n    if strings.Contains(err.Error(), \"status 404\") { return nil /* expired attachment */ }\n    return fmt.Errorf(\"download attachment: %w\", err)\n}","preventionTips":["Treat attachment URLs as ephemeral — download promptly after receiving the message","Classify status codes: 4xx non-retryable, 5xx retryable","Keep bot tokens fresh and scoped correctly","Log the status code for diagnostics"],"tags":["webex","http","download","api"],"backgroundTag":"http-error-response","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}