{"record":{"id":"0ef56abb7da56a1f","repo":"chenhg5/cc-connect","slug":"resource-too-large-body-exceeds-cap-d","errorCode":null,"errorMessage":"resource too large: body exceeds cap %d","messagePattern":"resource too large: body exceeds cap (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/feishu/resource_download.go","lineNumber":176,"sourceCode":"\t\tif !ok {\n\t\t\treturn nil, 0, fmt.Errorf(\"first-chunk: 206 without parseable Content-Range %q\", cr)\n\t\t}\n\t\tbody, err := io.ReadAll(io.LimitReader(resp.Body, 1))\n\t\tif err != nil {\n\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","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/feishu/resource_download.go#L158-L194","documentation":"Thrown when the server ignored the Range header and sent the whole file in one 200 response, and the body length exceeds resourceMaxBytes even after the LimitReader cap. Functionally identical to the body>cap guard in resourceDownloadStream, but raised inside resourceFetchFirstChunk on the 200 path; the difference is only where the size was observed.","triggerScenarios":"Feishu (or an intermediary) responds 200 with the full resource, io.ReadAll reads resourceMaxBytes+1 bytes, len(body) > resourceMaxBytes. Fires before the fallback logic in resourceDownloadStream can even see the probe result.","commonSituations":"User sends a very large file (e.g. 50+ MB) while resource_max_bytes is small; proxy stripping the Range header so the entire file always streams to the bot.","solutions":["Raise resource_max_bytes in config.toml to the maximum file size users are expected to send","Fix or remove the intermediary stripping the Range header so large files can be fetched chunk-wise","Handle the error at the bot layer by replying to the user that the file is too large to fetch"],"exampleFix":"// before (config.toml)\n# resource_max_bytes default (small)\n// after\n[platform.feishu]\nresource_max_bytes = 209715200  # 200 MiB","handlingStrategy":"validation","validationCode":"if meta.Size > maxResourceBytes { reply(\"file too large to fetch\"); return }","typeGuard":"null","tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"body exceeds cap\") {\n    replyToUser(\"This file is larger than my download limit (\" + human(maxResourceBytes) + \").\")\n}","preventionTips":["Size resource_max_bytes for the largest expected upload plus headroom","Cap download-triggering file types or announce limits in the bot help text","Investigate any proxy that strips Range (turning probes into full-body 200s)","Alert when this error frequency rises — it means users are hitting the limit"],"tags":["go","feishu","file-size","memory"],"backgroundTag":"file-size-limit-exceeded","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"}