{"record":{"id":"4a635850914cbdd0","repo":"valyala/fasthttp","slug":"brotli-excessive-input","errorCode":null,"errorMessage":"brotli: excessive input","messagePattern":"brotli: excessive input","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"brotli.go","lineNumber":201,"sourceCode":"\t}\n\tif err == nil && r.excessiveInput() {\n\t\treturn nn, errBrotliExcessiveInput\n\t}\n\treturn nn, err\n}\n\n// AppendUnbrotliBytes appends unbrotlied src to dst and returns the resulting dst.\nfunc AppendUnbrotliBytes(dst, src []byte) ([]byte, error) {\n\tw := &byteSliceWriter{b: dst}\n\t_, err := WriteUnbrotli(w, src)\n\treturn w.b, err\n}\n\n// errBrotliExcessiveInput is returned when a complete brotli stream is followed\n// by bytes that aren't part of it. github.com/andybalholm/brotli, the decoder\n// fasthttp used before, reported this with the same message; go-brrr ignores\n// the trailing bytes, so brotliSliceReader detects them instead.\nvar errBrotliExcessiveInput = errors.New(\"brotli: excessive input\")\n\n// brotliSliceReader hands the decoder everything but the final byte of b,\n// releasing that byte only once the decoder asks for more input. A brotli\n// stream is self-terminating and its final byte always carries stream bits, so\n// a decoder that succeeds without asking for the held back byte ended before\n// the end of b: the leftover is excessive input.\ntype brotliSliceReader struct {\n\tb []byte\n}\n\nfunc newBrotliSliceReader(b []byte) *brotliSliceReader {\n\treturn &brotliSliceReader{b: b}\n}\n\nfunc (r *brotliSliceReader) Read(p []byte) (int, error) {\n\tif len(r.b) > 1 {\n\t\t// Always withhold the final byte.\n\t\tn := copy(p, r.b[:len(r.b)-1])","sourceCodeStart":183,"sourceCodeEnd":219,"githubUrl":"https://github.com/valyala/fasthttp/blob/c96f600972c6f4a7a30d664257b340ebe9d60124/brotli.go#L183-L219","documentation":"errBrotliExcessiveInput is returned by writeUnbrotli when a complete brotli stream is followed by bytes that are not part of it. The decoder stops consuming input once the stream terminates; if leftover bytes remain in the input buffer, fasthttp reports them as excessive input. The message matches what the previously used andybalholm/brotli decoder reported; fasthttp's own reader detects it.","triggerScenarios":"Calling writeUnbrotli (e.g. via Response/Request body decompression of Content-Encoding: br) on a body where valid brotli data is concatenated with trailing garbage, padding, or a second stream appended without framing.","commonSituations":"Proxies or caches that truncate/append to compressed bodies; concatenating two brotli-compressed chunks and decompressing as one stream; a corrupted upload where extra bytes were appended after compression.","solutions":["Validate the source of the compressed data: decompress exactly one stream and strip/ignore known trailing bytes at the producer side.","If the extra bytes are benign (e.g. zero padding), slice the input to the exact stream length before decompressing or ignore the error deliberately.","Compare bytes decompressed so far with the expected content length to find where the corruption originates.","Re-fetch or re-request the body if it came from the network, since appended bytes usually indicate upstream corruption."],"exampleFix":"// before\nerr := writeUnbrotli(dst, corruptedBody) // brotli: excessive input\n// after\nif err := writeUnbrotli(dst, body[:len(body)-paddingLen]); err != nil {\n    return fmt.Errorf(\"brotli decode: %w\", err)\n}","handlingStrategy":"try-catch","validationCode":"// pre-check: decompress and compare consumed bytes with expected content length\nif expected := resp.Header.ContentLength(); expected > 0 && len(body) != expected {\n    return fmt.Errorf(\"body length %d != content-length %d\", len(body), expected)\n}","typeGuard":null,"tryCatchPattern":"var out bytes.Buffer\nerr := writeUnbrotli(&out, body)\nif err != nil && err.Error() == \"brotli: excessive input\" {\n    // tolerate known trailing padding\n    log.Warn(\"trailing bytes after brotli stream ignored\")\n} else if err != nil {\n    return err\n}","preventionTips":["Never concatenate separately compressed brotli payloads and decode as one stream","Verify Content-Length matches the compressed payload size","Strip padding at the producer, not the consumer","Re-fetch bodies that fail decoding instead of retrying the decode"],"tags":["brotli","compression","data-corruption"],"backgroundTag":"trailing-bytes-after-compressed-stream","analyzedSha":"c96f600972c6f4a7a30d664257b340ebe9d60124","analyzedAt":"2026-08-31T22:48:28.265Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}