{"record":{"id":"7030281644b70f4b","repo":"kovidgoyal/kitty","slug":"output-slice-too-small-need-at-least-d-got-d","errorCode":null,"errorMessage":"output slice too small: need at least %d, got %d","messagePattern":"output slice too small: need at least (.+?), got (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tools/utils/streaming_base64/api.go","lineNumber":41,"sourceCode":"\t\treturn CorruptInputError(int64(e) + chunkOffset)\n\t}\n\treturn err\n}\n\n// The size of output buffer needed for the provided size of input\nfunc (s *StreamingBase64Decoder) NeededOutputLen(input_len int) int {\n\treturn ((input_len + s.num_leftover) / 4) * 3\n}\n\n// Decode provided input, iterating in chunks. Each chunk is a slice from the\n// provided output buffer, which must be at least s.NeededOutputLen() in size.\nfunc (s *StreamingBase64Decoder) Decode(input []byte, output []byte) iter.Seq2[[]byte, error] {\n\t// Base64 decoding: 4 input bytes -> 3 output bytes.\n\t// We check if output is large enough for this chunk + any buffered data.\n\tmaxPossibleOutput := s.NeededOutputLen(len(input))\n\treturn func(yield func([]byte, error) bool) {\n\t\tif len(output) < maxPossibleOutput {\n\t\t\tyield(nil, fmt.Errorf(\"output slice too small: need at least %d, got %d\", maxPossibleOutput, len(output)))\n\t\t\treturn\n\t\t}\n\t\tcurrIn := input\n\t\toutOffset := 0\n\n\t\t// 1. Handle leftover bytes from previous call\n\t\tif s.num_leftover > 0 {\n\t\t\tneed := 4 - s.num_leftover\n\t\t\tif len(currIn) >= need {\n\t\t\t\tcopy(s.leftover[s.num_leftover:], currIn[:need])\n\n\t\t\t\t// Decode the bridge block\n\t\t\t\tn, err := base64.StdEncoding.Decode(output[outOffset:], s.leftover[:4])\n\t\t\t\tif err != nil {\n\t\t\t\t\tyield(nil, wrap_error(err, s.total_read-int64(s.num_leftover)))\n\t\t\t\t\treturn\n\t\t\t\t}\n","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/kovidgoyal/kitty/blob/6d5d0c440603ad9bdf6dcd599f73f6dde21acb44/tools/utils/streaming_base64/api.go#L23-L59","documentation":"StreamingBase64Decoder.Decode is an iterator (iter.Seq2) that writes decoded bytes into a caller-provided output slice. It precomputes maxPossibleOutput = NeededOutputLen(len(input)) (accounting for leftover buffered input bytes) and immediately yields this error if the output slice is smaller, without decoding anything.","triggerScenarios":"Calling Decode with an output buffer sized only for the current chunk while leftover bytes from a previous call exist, or any buffer smaller than 3*ceil(totalInput/4). E.g. passing make([]byte, len(input)/4*3) when 2 bytes were buffered from before.","commonSituations":"Callers reusing a fixed-size chunk buffer across streaming iterations and forgetting the decoder carries state; off-by-one sizing that ignores base64 padding semantics.","solutions":["Always size the buffer via s.NeededOutputLen(len(chunk)) before each call.","Allocate generously: make([]byte, base64.StdEncoding.DecodedLen(len(input))+8).","Check the first yielded error and grow the buffer, then retry the same chunk.","Read the API doc comment: the output must be at least NeededOutputLen()."],"exampleFix":"// before\nout := make([]byte, len(chunk)*3/4)\nfor decoded, err := range dec.Decode(chunk, out) { ... }\n// after\nout := make([]byte, dec.NeededOutputLen(len(chunk)))\nfor decoded, err := range dec.Decode(chunk, out) { ... }","handlingStrategy":"validation","validationCode":"out := make([]byte, dec.NeededOutputLen(len(chunk)))\nfor b, err := range dec.Decode(chunk, out) { ... }","typeGuard":null,"tryCatchPattern":"for b, err := range dec.Decode(chunk, out) {\n    if err != nil && strings.Contains(err.Error(), \"too small\") {\n        out = make([]byte, dec.NeededOutputLen(len(chunk)))\n        // retry same chunk\n    }\n}","preventionTips":["Always size buffers via NeededOutputLen per chunk.","Add slack: base64.StdEncoding.DecodedLen(len(in))+8.","Test with input lengths where len%4 != 0."],"tags":["go","base64","streaming","buffer-sizing"],"backgroundTag":"output-buffer-too-small","analyzedSha":"6d5d0c440603ad9bdf6dcd599f73f6dde21acb44","analyzedAt":"2026-08-27T14:20:20.142Z","schemaVersion":2},"datasetVersion":"2026-08-27T19:17:21.184Z"}