valeriansaliou/sonic · error · panic

buffer overflow ({}/{} bytes)

Error message

buffer overflow ({}/{} bytes)

What it means

The server's channel thread panics when a single incoming line would exceed MAX_LINE_SIZE (BUFFER_SIZE + LINE_END_GAP + 1). Sonic treats this as a client that does not implement back-pressure, so it terminates the connection thread to protect the buffer.

Source

Thrown at server/src/channel/handle.rs:209

                    }

                    // Check for buffer overflow.
                    // NOTE: To avoid a needless read of `MAX_LINE_SIZE` bytes,
                    //   we also ensure there is enough space for the line
                    //   separator. If there isn’t, next loop cycle will abort
                    //   because the line is too long anyway.
                    let separator_len = char::from(BUFFER_LINE_SEPARATOR).len_utf8();

                    if (buffer.len() + read.len()) < (MAX_LINE_SIZE - separator_len) {
                        buffer.extend(read);
                    } else {
                        // Do not continue, as there is too much pending data
                        // in the buffer. Most likely the client does not
                        // implement a proper back-pressure management system,
                        // thus we terminate it.
                        tracing::error!("closing channel thread because of buffer overflow");

                        panic!(
                            "buffer overflow ({}/{} bytes)",
                            buffer.len() + read.len(),
                            MAX_LINE_SIZE
                        );
                    }
                }
                Err(err) => {
                    tracing::error!("closing channel thread with traceback: {}", err);

                    panic!("closing channel");
                }
            }
        }
    }

    fn ensure_start(&self, mut stream: &TcpStream) -> Result<ChannelMode, ChannelHandleError> {
        #[allow(clippy::never_loop)]
        loop {

View on GitHub (pinned to e6a72da6a5)

Solutions

  1. Split large data into commands smaller than the server's MAX_LINE_SIZE (~ BUFFER_SIZE + LINE_END_GAP + 1 bytes)
  2. Chunk large PUSH payloads into multiple commands
  3. Reduce payload size (truncate/index only the needed text)
  4. If you control the deployment, raise the buffer size in the server config to fit your largest command

Example fix

// client
// before
conn.send(format!("PUSH collection bucket {}", huge_text)); // > MAX_LINE_SIZE
// after
for chunk in chunk_text(huge_text, MAX_LINE_SIZE) {
    conn.send(format!("PUSH collection bucket {}", chunk));
}
Defensive patterns

Strategy: validation

Validate before calling

const MAX_LINE_SIZE: usize = /* server BUFFER_SIZE + LINE_END_GAP + 1 */;
if payload.len() > MAX_LINE_SIZE {
    // split into chunked commands before sending
}

Try / catch

// server-side is a panic; clients should just keep under the limit
// catch the connection close if you drive the socket yourself
match stream.write_all(line.as_bytes()) {
    Ok(_) => {},
    Err(e) => reconnect(),
}

Prevention

When it happens

Trigger: A client sends one command/line whose size (buffer.len() + read.len()) exceeds MAX_LINE_SIZE over the TCP channel in handle_stream, e.g. a QUERY/PUSH payload larger than the server buffer without a newline terminator.

Common situations: Bulk-loading very large strings via PUSH; naive clients writing megabytes without chunking; misconfigured proxies concatenating frames; load-testing tools sending oversized lines.

Related errors


AI-assisted analysis of valeriansaliou/sonic@e6a72da6a5 (2026-09-01). Data as JSON: /api/errors/04bd2377768c0029. Report an issue: GitHub.