leptos-rs/leptos · error
Valid text format type with utf-8 comptabile string
Error message
Valid text format type with utf-8 comptabile string
What it means
Encoding::into_encoded_string serializes a payload whose FORMAT_TYPE is Format::Text by calling String::from_utf8 and panics if the bytes are not valid UTF-8. The library assumes any text-format codec (e.g. JSON, URL) always produces UTF-8, so a non-UTF-8 payload indicates a bug or corrupted data.
Source
Thrown at server_fn/src/lib.rs:827
Text,
}
/// A trait for types with an associated content type.
pub trait ContentType {
/// The MIME type of the data.
const CONTENT_TYPE: &'static str;
}
/// Data format representation
pub trait FormatType {
/// The representation type
const FORMAT_TYPE: Format;
/// Encodes data into a string.
fn into_encoded_string(bytes: Bytes) -> String {
match Self::FORMAT_TYPE {
Format::Binary => STANDARD_NO_PAD.encode(bytes),
Format::Text => String::from_utf8(bytes.into())
.expect("Valid text format type with utf-8 comptabile string"),
}
}
/// Decodes string to bytes
fn from_encoded_string(data: &str) -> Result<Bytes, DecodeError> {
match Self::FORMAT_TYPE {
Format::Binary => {
STANDARD_NO_PAD.decode(data).map(|data| data.into())
}
Format::Text => Ok(Bytes::copy_from_slice(data.as_bytes())),
}
}
}
/// A trait for types that can be encoded into a bytes for a request body.
pub trait Encodes<T>: ContentType + FormatType {
/// The error type that can be returned if the encoding fails.
type Error: Display + Debug;View on GitHub (pinned to 32d20f6c9d)
Solutions
- Verify the serializer/Encoder used with the text format always emits UTF-8 (JSON, URL-encoded, etc.)
- Convert the payload to UTF-8 before passing it to into_encoded_string, or use String::from_utf8 lossily on your side first
- Switch the codec to Format::Binary (base64 path) if the data is inherently binary
- Fix the source of non-UTF-8 bytes (file read encoding, database column charset)
Example fix
// before let s = MyFormat::into_encoded_string(latin1_bytes); // panics // after let bytes = Bytes::from(String::from_utf8_lossy(&latin1_bytes).into_owned()); let s = MyFormat::into_encoded_string(bytes);
Defensive patterns
Strategy: try-catch
Validate before calling
fn is_valid_utf8(bytes: &[u8]) -> bool { std::str::from_utf8(bytes).is_ok() }
// call before: assert!(is_valid_utf8(&bytes)); Type guard
fn as_utf8(bytes: Bytes) -> Option<String> {
String::from_utf8(bytes.into()).ok()
} Try / catch
// panic-based API: validate first; if unavoidable, isolate via catch_unwind
let result = std::panic::catch_unwind(|| MyTextFormat::into_encoded_string(bytes.clone()));
match result {
Ok(s) => s,
Err(_) => MyBinaryFormat::into_encoded_string(bytes), // fall back to base64 path
} Prevention
- Only use Format::Text codecs with UTF-8-emitting serializers
- Normalize file/DB encodings to UTF-8 upstream
- Prefer Format::Binary for any possibly-non-UTF-8 payload
- Add a debug assertion is_valid_utf8 before encoding in tests
When it happens
Trigger: Invoking into_encoded_string on a text-format codec when the Bytes argument came from binary data, a custom Encoder that returns non-UTF-8 output, or corrupted/transcoded bytes (e.g. Latin-1 encoded source).
Common situations: Custom server fn encoding registered with Format::Text but a serializer emitting raw binary; bytes read from a file/DB in a non-UTF-8 encoding; middleware re-encoding request bodies; mixing Binary and Text formats when piping through caches.
Related errors
- Executor::spawn called, but no global 'spawn' function is co
- Executor::spawn_local called, but no global 'spawn_local' fu
- At {caller}, tried to spawn a Future with Executor::spawn()
- At {caller}, tried to spawn a Future with Executor::spawn_lo
- called `unwrap_{variant}()` on a non-`{variant}` variant of
AI-assisted analysis of leptos-rs/leptos@32d20f6c9d (2026-09-01).
Data as JSON: /api/errors/f81f28421e426459.
Report an issue: GitHub.