databendlabs/databend · error

Expected to have char

Error message

Expected to have char '{}', got '{:?}' at pos {}

What it means

This is a parsing error thrown by `CursorReadBytesExt::must_ignore_byte` in databend-common-io. When a caller (e.g. `read_quoted_text` or `fast_read_quoted_text`) expects the next byte in the input cursor to be a specific character (such as an opening/closing quote), the helper consumes it if it matches; if the cursor's next byte differs, the parser aborts with the expected char, the actual peeked byte, and the cursor position.

Solutions

  1. Inspect the 'at pos N' value in the message and print the input around position N to see the actual byte encountered.
  2. Ensure the input begins and ends with a matching quote character before calling read_quoted_text.
  3. Normalize quote style (use ' or " consistently) and strip trailing whitespace/newlines from the input.
  4. If parsing user input, validate or pre-escape the literal before handing it to the cursor parser.

Example fix

// before: parser gets non-quote byte
let s = "abc".as_bytes().read_quoted_text(&mut buf)?;
// after: ensure the literal is quoted first
let input = "abc";
let s = format!("\"{}\"", input).as_bytes().read_quoted_text(&mut buf)?;
Defensive patterns

Strategy: validation

Validate before calling

fn ensure_quoted(input: &[u8]) -> bool {
    let q = input.first()?;
    (*q == b'\'' || *q == b'"') && input.last() == Some(q) && input.len() >= 2
}

Type guard

fn is_quoted_literal(s: &str) -> bool {
    let b = s.as_bytes();
    b.len() >= 2 && (b[0] == b'\'' || b[0] == b'"') && b[b.len()-1] == b[0]
}

Try / catch

match input.as_bytes().read_quoted_text(&mut buf) {
    Ok(s) => s,
    Err(e) => { log::warn!("literal parse failed: {e}"); return default_literal(); }
}

Prevention

When it happens

Trigger: Calling `read_quoted_text`/`fast_read_quoted_text` on input where the byte at the current cursor position is not the expected delimiter — e.g. parsing an opening quote but the input has no quote at the start, a closing quote is missing or replaced by another character, or a malformed escape sequence shifted the cursor onto an unexpected byte.

Common situations: Parsing SQL-ish or serialized string values from user-supplied text (query literals, config files, CSV-like payloads) where quotes are unbalanced, single vs double quotes are mixed, or trailing whitespace/control characters sit where the parser expects a delimiter.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of databendlabs/databend@288d84d76e (2026-09-11). Data as JSON: /api/errors/0efb44f522b7e888. Report an issue: GitHub.

Appendix: source

Thrown at src/common/io/src/cursor_ext/cursor_read_bytes_ext.rs:145

        self.ignore(|c| c == b)
    }

    fn ignore_bytes(&mut self, bs: &[u8]) -> bool {
        let available = Cursor::split(self).1;
        let len = bs.len();
        if available.len() < len {
            return false;
        }
        let eq = available[..len].iter().zip(bs).all(|(x, y)| x == y);
        if eq {
            BufRead::consume(self, len);
        }
        eq
    }

    fn must_ignore_byte(&mut self, b: u8) -> Result<()> {
        if !self.ignore_byte(b) {
            return Err(std::io::Error::new(
                ErrorKind::InvalidData,
                format!(
                    "Expected to have char '{}', got '{:?}' at pos {}",
                    b as char,
                    self.peek(),
                    self.position()
                ),
            ));
        }
        Ok(())
    }

    fn ignore_insensitive_bytes(&mut self, bs: &[u8]) -> bool {
        let available = Cursor::split(self).1;
        let len = bs.len();
        if available.len() < len {
            return false;
        }

View on GitHub (pinned to 288d84d76e)