{"record":{"id":"ed2eb9713aec70b2","repo":"chenhg5/cc-connect","slug":"resource-chunk-offset-d-w","errorCode":null,"errorMessage":"resource chunk offset=%d: %w","messagePattern":"resource chunk offset=(.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/feishu/resource_download.go","lineNumber":204,"sourceCode":"// 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 {\n\t\t\tend = total - 1\n\t\t}\n\t\tn, err := p.resourceRangeChunk(ctx, token, messageID, fileKey, resType, offset, end, total)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"resource chunk offset=%d: %w\", offset, err)\n\t\t}\n\t\tbuf.Write(n)\n\t\tchunks++\n\t\tif err := ctx.Err(); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tif int64(buf.Len()) != total {\n\t\treturn nil, fmt.Errorf(\"resource size mismatch: assembled=%d expected=%d\", buf.Len(), total)\n\t}\n\tslog.Info(p.tag()+\": resource chunked download complete\",\n\t\t\"file_key\", fileKey, \"type\", resType, \"total\", total, \"chunks\", chunks,\n\t\t\"chunk_size\", chunkSize)\n\treturn buf.Bytes(), nil\n}\n\n// parseContentRangeTotal extracts the \"total\" field from a Content-Range","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/feishu/resource_download.go#L186-L222","documentation":"This error wraps a failure that occurred while downloading one non-first chunk of a Feishu message resource (file/image) via an HTTP Range request in resourceFetchRemainingChunks. The library chunks large downloads after a size probe and fetches each chunk with resourceRangeChunk; when any chunk fetch fails, it wraps the underlying error with the chunk's byte offset so callers can tell where the assembly broke. The offset is the start byte of the chunk that failed.","triggerScenarios":"Calling resourceDownloadStream for a resource whose probed total size exceeds the chunk size, causing resourceFetchRemainingChunks to issue Range GETs; a chunk's HTTP request fails (network error, non-206 status, server rejecting Range, auth token expiring mid-download), so resourceRangeChunk returns an error.","commonSituations":"Flaky network or proxy dropping long multi-chunk downloads; Feishu API returning 4xx/5xx on Range requests because the tenant_access_token expired between chunks; server ignoring the Range header and returning 200 instead of 206; corporate firewall blocking partial-content requests.","solutions":["Retry the download; resourceRangeChunk already retries transient errors up to maxTransientRetries, so persistent failures usually mean network or auth issues — refresh the tenant_access_token and retry","Check that the token passed to resourceDownloadStream is a valid, unexpired tenant_access_token with im:resource read permission","Verify network/proxy allows HTTP Range (partial content) requests to open.feishu.cn","Inspect the wrapped cause (%w) in logs — the underlying resourceRangeChunk error (status code, network error) names the real problem","If the file is small enough, rely on resourceSingleGet (used when the size probe fails) which avoids chunking entirely"],"exampleFix":"// before: single call, no token refresh on long downloads\ndata, err := p.resourceDownloadStream(ctx, staleToken, msgID, fileKey, resType)\n// after: obtain a fresh token before starting a chunked download\ntoken, err := p.freshTenantToken(ctx)\nif err != nil { return nil, err }\ndata, err := p.resourceDownloadStream(ctx, token, msgID, fileKey, resType)","handlingStrategy":"retry","validationCode":"// before download\nif token == \"\" { return fmt.Errorf(\"missing tenant_access_token\") }\nif msgID == \"\" || fileKey == \"\" { return fmt.Errorf(\"empty message_id/file_key\") }","typeGuard":null,"tryCatchPattern":"data, err := p.resourceDownloadStream(ctx, token, msgID, fileKey, resType)\nif err != nil {\n    if strings.Contains(err.Error(), \"resource chunk offset=\") {\n        // refresh token, wait briefly, retry once\n        return retryDownload(ctx, msgID, fileKey, resType)\n    }\n    return err\n}","preventionTips":["Always fetch a fresh tenant_access_token before long downloads","Ensure network path supports HTTP Range requests (no Range-stripping proxies)","Monitor Feishu API status for incidents before large transfers"],"tags":["network","http","chunked-download","feishu"],"backgroundTag":"http-request-failed","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"}