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

  1. Always size the buffer via s.NeededOutputLen(len(chunk)) before each call.
  2. Allocate generously: make([]byte, base64.StdEncoding.DecodedLen(len(input))+8).
  3. Check the first yielded error and grow the buffer, then retry the same chunk.
  4. 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

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


AI-assisted analysis of kovidgoyal/kitty@6d5d0c4406 (2026-08-27). Data as JSON: /api/errors/7030281644b70f4b. Report an issue: GitHub.