{"record":{"id":"d77cdd39b5d68f00","repo":"chenhg5/cc-connect","slug":"first-chunk-unexpected-status-d","errorCode":null,"errorMessage":"first-chunk: unexpected status %d","messagePattern":"first-chunk: unexpected status (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/feishu/resource_download.go","lineNumber":181,"sourceCode":"\t\t\treturn nil, 0, fmt.Errorf(\"read first-chunk body: %w\", err)\n\t\t}\n\t\treturn body, total, nil\n\n\tcase http.StatusOK:\n\t\t// Server ignored Range and sent the full body. We deliberately\n\t\t// honour it and skip the chunked loop — this matches pre-#1741\n\t\t// behaviour for files small enough that Feishu doesn't truncate.\n\t\tbody, err := io.ReadAll(io.LimitReader(resp.Body, p.resourceMaxBytes+1))\n\t\tif err != nil {\n\t\t\treturn nil, 0, fmt.Errorf(\"read full body: %w\", err)\n\t\t}\n\t\tif int64(len(body)) > p.resourceMaxBytes {\n\t\t\treturn nil, 0, fmt.Errorf(\"resource too large: body exceeds cap %d\", p.resourceMaxBytes)\n\t\t}\n\t\treturn body, 0, nil\n\n\tdefault:\n\t\treturn nil, 0, fmt.Errorf(\"first-chunk: unexpected status %d\", resp.StatusCode)\n\t}\n}\n\n// resourceFetchRemainingChunks loops Range GETs starting after the first\n// byte, concatenates them with `first`, and verifies the total size matches\n// what the probe advertised.\nfunc (p *Platform) resourceFetchRemainingChunks(ctx context.Context, token, messageID, fileKey, resType string, total int64, first []byte) ([]byte, error) {\n\tbuf := bytes.NewBuffer(make([]byte, 0, total))\n\tbuf.Write(first)\n\n\tchunkSize := p.resourceChunkSize\n\tif chunkSize > resourceRangeMaxRangeHeaderBytes {\n\t\tchunkSize = resourceRangeMaxRangeHeaderBytes\n\t}\n\tchunks := 1 // count the first byte we already have\n\tfor offset := int64(1); offset < total; offset += chunkSize {\n\t\tend := offset + chunkSize - 1\n\t\tif end >= total {","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/feishu/resource_download.go#L163-L199","documentation":"Thrown when the first-chunk probe receives any HTTP status other than 200 or 206 — e.g. 401/403 (auth rejected), 404 (message or file_key gone), or 5xx. The chunked downloader only understands those two success shapes and refuses to interpret anything else, giving the raw status code for diagnosis. The caller then falls back to resourceSingleGet, which reports a fuller status+body error if it also fails.","triggerScenarios":"resourceDownloadHTTP returns 3xx/4xx/5xx on the Range bytes=0-0 GET: expired tenant token mid-flight, wrong messageID/fileKey, resource already deleted, or Feishu server error.","commonSituations":"Bot's app removed from the chat (403), user deletes the message/file before the bot downloads it (404), token cache expiry race (401), Feishu incident (5xx).","solutions":["Read the fallback resourceSingleGet error, which includes a body snippet with Feishu's code/msg","On 401/403: re-check app permissions (im:resource) and tenant token minting; the next download remints the token","On 404: the message or file_key no longer exists — treat as unrecoverable and inform the user","On 5xx: retry later; check Feishu status/uptime"],"exampleFix":"null","handlingStrategy":"fallback","validationCode":"null","typeGuard":"null","tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"unexpected status\") {\n    var apiErr *feishu.APIError\n    if errors.As(err, &apiErr) && apiErr.Code == 99991672 { /* 404: file gone */ }\n}","preventionTips":["Download resources promptly after message receipt — files can be deleted later (404)","Keep app token minting healthy so downloads don't race token expiry (401)","Maintain im:resource scope grants; re-verify after app permission changes (403)","Retry 5xx-class failures with backoff; treat 4xx as terminal and user-facing"],"tags":["go","feishu","http","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-14T05:17:10.506Z"}