risingwavelabs/risingwave · error · io::Error

InvalidInput

InvalidInput

Error message

Input end error

What it means

This error is thrown when parsing the frontend startup message payload: `FeStartupMessage::build_with_payload` first decodes the payload as UTF-8, and if the bytes are not valid UTF-8, the `Utf8Error` is wrapped with the 'Input end error' context and an `ErrorKind::InvalidInput`. The PostgreSQL wire protocol requires startup parameters to be UTF-8 encoded null-terminated strings, so undecodable bytes are rejected before the config map is built.

Source

Thrown at src/utils/pgwire/src/pg_message.rs:81

#[derive(Debug)]
pub enum ServerThrottleReason {
    TooLargeMessage,
    TooManyMemoryUsage,
}

#[derive(Debug)]
pub struct FeStartupMessage {
    pub config: HashMap<String, String>,
}

impl FeStartupMessage {
    pub fn build_with_payload(payload: &[u8]) -> Result<Self> {
        let config = match std::str::from_utf8(payload) {
            Ok(v) => Ok(v.strip_suffix('\0').unwrap_or(v)),
            Err(err) => Err(Error::new(
                ErrorKind::InvalidInput,
                anyhow!(err).context("Input end error"),
            )),
        }?;
        let mut map = HashMap::new();
        let config: Vec<&str> = config.split_terminator('\0').collect();
        if config.len() % 2 == 1 {
            return Err(Error::new(
                ErrorKind::InvalidInput,
                "Invalid input config: odd number of config pairs",
            ));
        }
        config.chunks(2).for_each(|chunk| {
            map.insert(chunk[0].to_owned(), chunk[1].to_owned());
        });
        Ok(FeStartupMessage { config: map })
    }
}

/// Query message contains the string sql.

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Configure the client/driver to use UTF-8 encoding for connection parameters (e.g. `client_encoding=UTF8`)
  2. Check that no proxy or middleware re-encodes the startup packet bytes
  3. Ensure database/user/application_name values contain only characters representable in UTF-8 as sent by the driver
  4. If writing a custom client, encode all startup-payload strings as UTF-8 with NUL terminators

Example fix

// before (custom client, local codepage)
payload.write_all(username.encode(Encoding::GBK).as_slice());
// after
payload.write_all(username.as_bytes()); // Rust str is UTF-8
Defensive patterns

Strategy: try-catch

Validate before calling

fn is_valid_utf8_payload(payload: &[u8]) -> bool {
    std::str::from_utf8(payload).is_ok()
}

Try / catch

match std::str::from_utf8(payload) {
    Ok(s) => s,
    Err(e) => {
        // log the byte offset: e.valid_up_to(), reject connection
        return Err(...);
    }
}

Prevention

When it happens

Trigger: A client sends a startup packet whose payload contains bytes that fail `std::str::from_utf8` — e.g. a driver sending strings in a non-UTF-8 legacy encoding (Latin-1, GBK) in parameters like `user`, `database`, or `application_name`.

Common situations: Legacy clients or misconfigured JDBC/ODBC drivers with non-UTF-8 client encoding; passwords or database names containing non-ASCII characters encoded in a local codepage; corrupted or truncated TCP payloads from a faulty proxy.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/07b4412a8ce9ca6a. Report an issue: GitHub.