risingwavelabs/risingwave · error · io::Error
failed to write_char
Error message
failed to write_char [{c}] What it means
In write_str_bytes (pg_message.rs:797), while escaping a string into the outgoing buffer, the fallback branch calls std::fmt::Write::write_char on the BytesMut-backed formatter; if the underlying write fails, the error is wrapped as Error::other("failed to write_char [{c}]"). In practice this signals an internal failure while serializing a string literal into a backend message, not a client-input problem.
Solutions
- Reproduce with the logged character [c] and check whether the escaping logic should be handling it in an earlier branch (e.g. quote/backslash handling) rather than the fallback write_char.
- Check server logs/monitoring for memory pressure or allocation failures at serialization time; a failed append to the buffer is the usual root cause.
- If a custom writer or patched BytesMut wrapper is in use, verify its fmt::Write implementation never returns Err for valid chars.
- This is effectively an internal invariant violation — if reproducible with normal data, file a bug with the offending character and message type.
Example fix
// before: fallback may fail silently on exotic chars
std::fmt::Write::write_char(buf, c).map_err(|_| Error::other(anyhow!("failed to write_char [{c}]")))?;
// after: ensure capacity before writing, avoiding transient append failure
buf.reserve(c.len_utf8());
std::fmt::Write::write_char(buf, c).map_err(|_| Error::other(anyhow!("failed to write_char [{c}]")))?; Defensive patterns
Strategy: try-catch
Try / catch
// serialization path
if let Err(e) = msg.write(&mut buf).await {
if e.to_string().starts_with("failed to write_char") {
log::error!("internal serialization failure: {}", e);
// do not send the partially built buffer; reset and close
buf.clear();
}
return Err(e.into());
} Prevention
- Reserve buffer capacity before writing large response strings to avoid append failures.
- Keep BytesMut's fmt::Write implementation unmodified and well-tested.
- Review the escaping branches so expected special chars are handled explicitly, not via fallback.
- Treat any occurrence as a bug report trigger: capture the character and message type.
When it happens
Trigger: Calling the message write path (FeMessage::write / serialization of a response containing a string field) when the fmt::Write impl over the byte buffer returns Err — essentially only on buffer allocation/append failure; a normal char always succeeds, so this surfaces as an internal invariant issue during response serialization.
Common situations: Writing error/data row responses containing unusual characters where escaping hits an unexpected branch; running out of memory while growing the response buffer; bugs in a custom writer wrapped around BytesMut.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Avro error
- avro parse unexpected eof
- cannot derive generated columns or the `_rw_timestamp`…
- cannot serialize Document to Vec
- config deserialization error
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/e35f37f8c3595f74.
Report an issue: GitHub.
Appendix: source
Thrown at src/utils/pgwire/src/pg_message.rs:797
for c in s.as_str().chars() {
// As suggested in https://en.wikipedia.org/wiki/Tab-separated_values
// we only escape "\t\b\r\\"
match c {
'\t' => {
buf.put_slice(b"\\t");
}
'\n' => {
buf.put_slice(b"\\n");
}
'\r' => {
buf.put_slice(b"\\r");
}
'\\' => {
buf.put_slice(b"\\\\");
}
_ => {
std::fmt::Write::write_char(buf, c).map_err(|_| {
Error::other(anyhow!("failed to write_char [{c}]"))
})?;
}
}
}
Ok(())
}
match row.values() {
[] => {}
[first, rest @ ..] => {
write_str_bytes(buf, first)?;
for rest in rest {
buf.put_u8(b'\t');
write_str_bytes(buf, rest)?;
}
}
}
buf.put_u8(b'\n');View on GitHub (pinned to 6469eb736d)