{"record":{"id":"486d38fa0925b6dd","repo":"sipeed/picoclaw","slug":"image-exceeds-d-byte-limit","errorCode":null,"errorMessage":"image exceeds %d byte limit","messagePattern":"image exceeds (.+?) byte limit","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/channels/pico/pico.go","lineNumber":1407,"sourceCode":"\tif !strings.HasPrefix(mediaURL, \"data:image/\") {\n\t\treturn fmt.Errorf(\"only inline image data URLs are supported\")\n\t}\n\n\theader, data, found := strings.Cut(mediaURL, \",\")\n\tif !found || strings.TrimSpace(data) == \"\" {\n\t\treturn fmt.Errorf(\"image data URL is malformed\")\n\t}\n\tif !strings.Contains(header, \";base64\") {\n\t\treturn fmt.Errorf(\"image data URL must be base64 encoded\")\n\t}\n\tmimeType, _, _ := strings.Cut(strings.TrimPrefix(header, \"data:\"), \";\")\n\tif _, ok := allowedInlineImageMIMETypes[mimeType]; !ok {\n\t\treturn fmt.Errorf(\"unsupported image format: %s\", mimeType)\n\t}\n\n\tdata = strings.TrimSpace(data)\n\tif base64.StdEncoding.DecodedLen(len(data)) > config.DefaultMaxMediaSize {\n\t\treturn fmt.Errorf(\"image exceeds %d byte limit\", config.DefaultMaxMediaSize)\n\t}\n\tif _, err := base64.StdEncoding.DecodeString(data); err != nil {\n\t\treturn fmt.Errorf(\"invalid base64 image data\")\n\t}\n\n\treturn nil\n}\n\n// setContextUsagePayload adds context window usage stats to a pico payload.\nfunc setContextUsagePayload(payload map[string]any, u *bus.ContextUsage) {\n\tif u == nil {\n\t\treturn\n\t}\n\tpayload[\"context_usage\"] = map[string]any{\n\t\t\"used_tokens\":         u.UsedTokens,\n\t\t\"total_tokens\":        u.TotalTokens,\n\t\t\"history_tokens\":      u.HistoryTokens,\n\t\t\"compress_at_tokens\":  u.CompressAtTokens,","sourceCodeStart":1389,"sourceCodeEnd":1425,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/channels/pico/pico.go#L1389-L1425","documentation":"Thrown by validateInlineImageDataURL in the pico channel when an inline data:image/...;base64,... payload would decode to more than config.DefaultMaxMediaSize (20 MB, defined at pkg/config/config.go:452). The check uses base64.StdEncoding.DecodedLen(len(data)), i.e. the upper-bound decoded size of the base64 body, not the raw string length. The library enforces this to keep pico payloads (and the peer's upload) within a fixed media budget.","triggerScenarios":"Calling the pico send path with an OutboundMessage media entry whose data URL body decodes to >20971520 bytes; e.g. embedding a 30 MB PNG as base64 (base64 inflates it ~33% more on the wire). DecodedLen(len(data)) is computed on the trimmed base64 body after the comma in the data URL.","commonSituations":"Agents or tools inlining screenshots/photos as data URLs without downscaling; pasting a base64 image copied from an HTML page; switching from URL-referenced media to inline media; lowering/raising DefaultMaxMediaSize in a fork and forgetting the pico validator uses the same constant.","solutions":["Downscale or re-encode the image (JPEG/WebP quality ~80) so the decoded size is under 20 MB before building the data URL.","Send the image as a URL reference or media ref instead of an inline base64 data URL, if the pico flow supports it.","If you control the build, adjust config.DefaultMaxMediaSize (pkg/config/config.go:452) — but the remote pico peer enforces its own limit too.","Check for accidental double-base64 encoding (data that is already base64 being encoded again), which inflates size ~33%."],"exampleFix":"// before: raw 40 MB screenshot inlined directly\ndataURL := \"data:image/png;base64,\" + base64.StdEncoding.EncodeToString(rawPNG)\n\n// after: keep the decoded size under the 20 MB limit first\nconst max = 20 * 1024 * 1024 // config.DefaultMaxMediaSize\nif len(rawPNG) > max {\n    rawPNG = downscaleAndReencode(rawPNG) // or return an error / use a URL ref\n}\ndataURL := \"data:image/png;base64,\" + base64.StdEncoding.EncodeToString(rawPNG)","handlingStrategy":"validation","validationCode":"// before sending, check the decoded size against the same limit the validator uses\nconst maxMedia = 20 * 1024 * 1024 // config.DefaultMaxMediaSize\nfunc tooLarge(dataURL string) bool {\n    if _, body, ok := strings.Cut(dataURL, \",\"); ok {\n        return base64.StdEncoding.DecodedLen(len(strings.TrimSpace(body))) > maxMedia\n    }\n    return false\n}","typeGuard":null,"tryCatchPattern":"// after send: if err != nil && strings.Contains(err.Error(), \"byte limit\") -> tell the sender to shrink the image; do not retry unchanged","preventionTips":["Downscale/re-encode images before inlining them as data URLs","Prefer media refs or https URLs over inline base64 for large images","Remember the limit applies to the DECODED size, not the base64 string length"],"tags":["pico","image","base64","size-limit","validation"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}