nats-io/nats-server · error
short write on body (%d != %d)
Error message
short write on body (%d != %d)
What it means
This error is thrown by LocalCache.Compress in server/ocsp_responsecache.go when the s2 (Snappy) compression writer consumed fewer bytes than the full body length during io.CopyN. It means the OCSP response body was not fully written into the compression buffer, producing a truncated compressed record. The library throws it to prevent storing a silently corrupt cache entry.
Source
Thrown at server/ocsp_responsecache.go:352
case ocsp.Good:
c.stats.Goods++
case ocsp.Revoked:
c.stats.Revokes++
case ocsp.Unknown:
c.stats.Unknowns++
}
}
}
func (c *LocalCache) Compress(buf []byte) ([]byte, error) {
bodyLen := int64(len(buf))
var output bytes.Buffer
writer := s2.NewWriter(&output)
input := bytes.NewReader(buf[:bodyLen])
if n, err := io.CopyN(writer, input, bodyLen); err != nil {
return nil, fmt.Errorf(certidp.ErrCannotWriteCompressed, err)
} else if n != bodyLen {
return nil, fmt.Errorf(certidp.ErrTruncatedWrite, n, bodyLen)
}
if err := writer.Close(); err != nil {
return nil, fmt.Errorf(certidp.ErrCannotCloseWriter, err)
}
return output.Bytes(), nil
}
func (c *LocalCache) Decompress(buf []byte) ([]byte, error) {
bodyLen := int64(len(buf))
input := bytes.NewReader(buf[:bodyLen])
reader := io.NopCloser(s2.NewReader(input))
output, err := io.ReadAll(reader)
if err != nil {
return nil, fmt.Errorf(certidp.ErrCannotReadCompressed, err)
}
return output, reader.Close()
}
View on GitHub (pinned to 3a66a489d2)
Solutions
- Log n and bodyLen and inspect the buffer passed to Put; ensure buf[:bodyLen] actually contains bodyLen bytes.
- Verify the upstream read that produced buf filled it completely before calling Put.
- Replace the short-write path with io.Copy into the writer and check the writer's error instead of assuming bodyLen.
- If persistent, bypass compression (store uncompressed) or regenerate the OCSP response and retry Put.
Example fix
// before
if n, err := io.CopyN(writer, input, bodyLen); err != nil {
return nil, fmt.Errorf(certidp.ErrCannotWriteCompressed, err)
} else if n != bodyLen {
return nil, fmt.Errorf(certidp.ErrTruncatedWrite, n, bodyLen)
}
// after
if n, err := io.CopyN(writer, input, bodyLen); err != nil {
return nil, fmt.Errorf(certidp.ErrCannotWriteCompressed, err)
} else if n != bodyLen {
// fall back to storing the raw response rather than a truncated record
return buf, nil
} Defensive patterns
Strategy: validation
Validate before calling
// before calling Put
if int64(len(buf)) < bodyLen {
return fmt.Errorf("buffer too small: have %d, need %d", len(buf), bodyLen)
} Type guard
func isCompleteBody(buf []byte, bodyLen int64) bool {
return int64(len(buf)) >= bodyLen
} Prevention
- Always fully read the OCSP response into buf before Put.
- Assert len(buf) >= bodyLen before compressing.
- Never reuse a partially-filled buffer across Put calls.
When it happens
Trigger: Calling LocalCache.Put on an OCSP response where the input reader (bytes.NewReader over buf[:bodyLen]) yields fewer bytes than bodyLen, or the s2 writer errors partway through the copy so io.CopyN returns n < bodyLen.
Common situations: A buffer whose declared bodyLen exceeds the data actually read from the OCSP responder response; a slice mismatch after partial reads; s2 writer returning a short write under memory pressure.
Related errors
- error closing compression writer: %w
- error reading compression reader: %w
- error writing to compression writer: %w
- short write on body (%d != %d)
- error closing compression writer: %w
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/0d8527d775b911d9.
Report an issue: GitHub.