kovidgoyal/kitty · error
output slice too small: need at least %d, got %d
Error message
output slice too small: need at least %d, got %d
What it means
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.
Source
Thrown at tools/utils/streaming_base64/api.go:41
return CorruptInputError(int64(e) + chunkOffset)
}
return err
}
// The size of output buffer needed for the provided size of input
func (s *StreamingBase64Decoder) NeededOutputLen(input_len int) int {
return ((input_len + s.num_leftover) / 4) * 3
}
// Decode provided input, iterating in chunks. Each chunk is a slice from the
// provided output buffer, which must be at least s.NeededOutputLen() in size.
func (s *StreamingBase64Decoder) Decode(input []byte, output []byte) iter.Seq2[[]byte, error] {
// Base64 decoding: 4 input bytes -> 3 output bytes.
// We check if output is large enough for this chunk + any buffered data.
maxPossibleOutput := s.NeededOutputLen(len(input))
return func(yield func([]byte, error) bool) {
if len(output) < maxPossibleOutput {
yield(nil, fmt.Errorf("output slice too small: need at least %d, got %d", maxPossibleOutput, len(output)))
return
}
currIn := input
outOffset := 0
// 1. Handle leftover bytes from previous call
if s.num_leftover > 0 {
need := 4 - s.num_leftover
if len(currIn) >= need {
copy(s.leftover[s.num_leftover:], currIn[:need])
// Decode the bridge block
n, err := base64.StdEncoding.Decode(output[outOffset:], s.leftover[:4])
if err != nil {
yield(nil, wrap_error(err, s.total_read-int64(s.num_leftover)))
return
}
View on GitHub (pinned to 6d5d0c4406)
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().
Example fix
// before
out := make([]byte, len(chunk)*3/4)
for decoded, err := range dec.Decode(chunk, out) { ... }
// after
out := make([]byte, dec.NeededOutputLen(len(chunk)))
for decoded, err := range dec.Decode(chunk, out) { ... } Defensive patterns
Strategy: validation
Validate before calling
out := make([]byte, dec.NeededOutputLen(len(chunk)))
for b, err := range dec.Decode(chunk, out) { ... } Try / catch
for b, err := range dec.Decode(chunk, out) {
if err != nil && strings.Contains(err.Error(), "too small") {
out = make([]byte, dec.NeededOutputLen(len(chunk)))
// retry same chunk
}
} Prevention
- Always size buffers via NeededOutputLen per chunk.
- Add slack: base64.StdEncoding.DecodedLen(len(in))+8.
- Test with input lengths where len%4 != 0.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Did not receive expected streaming response
- Too much data being sent
- No stream_id in rc payload
- Too much piped data
- Canceled by user
AI-assisted analysis of kovidgoyal/kitty@6d5d0c4406 (2026-08-27).
Data as JSON: /api/errors/7030281644b70f4b.
Report an issue: GitHub.