databendlabs/databend · error

Expected to have terminated string literal after quota

Error message

Expected to have terminated string literal after quota {:?}, while consumed buf: {:?}

What it means

Thrown by `CursorReadStringExt::read_quoted_text` when the closing quote for a string literal is never found. The parser consumes bytes into `buf` until EOF (or a non-continuation branch breaks the loop) and then reports the expected quote character plus everything it consumed, so the developer can see how far parsing got.

Solutions

  1. Check the consumed buffer in the message — the missing closing quote is at the end of that content.
  2. Ensure the literal is wrapped in a matching pair of the same quote character.
  3. Fix the upstream producer/serializer so it always emits balanced quotes.
  4. Validate literal balance with a quick count of unescaped quote chars before parsing.

Example fix

// before
let input = "'unterminated value";
// after
let input = "'unterminated value'";
Defensive patterns

Strategy: validation

Validate before calling

fn balanced_quotes(input: &str, quote: char) -> bool {
    let mut escaped = false;
    let mut count = 0;
    for c in input.chars() {
        if escaped { escaped = false; continue; }
        match c { '\\' => escaped = true, _ if c == quote => count += 1, _ => {} }
    }
    count % 2 == 0
}

Type guard

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

Try / catch

match read_quoted_text(&mut buf) {
    Ok(s) => s,
    Err(e) => {
        // message includes the consumed buffer; surface it for diagnostics
        return Err(anyhow!("unterminated literal: {e}"));
    }
}

Prevention

When it happens

Trigger: Calling `read_quoted_text` with a quote character (e.g. `'`) on input that opens the quote but never closes it: the entire remaining buffer is consumed and the error names the quote char and consumed buffer.

Common situations: Malformed SQL/text literals pasted from editors that stripped the closing quote, multi-line strings split across lines, or serialization bugs that emit one quote instead of two.

Related errors


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

Appendix: source

Thrown at src/common/io/src/cursor_ext/cursor_read_string_ext.rs:79

                match c {
                    b'n' => buf.push(b'\n'),
                    b't' => buf.push(b'\t'),
                    b'r' => buf.push(b'\r'),
                    b'0' => buf.push(b'\0'),
                    b'\'' => buf.push(b'\''),
                    b'\"' => buf.push(b'\"'),
                    b'\\' => buf.push(b'\\'),
                    b'/' => buf.push(b'/'),
                    _ => {
                        buf.push(b'\\');
                        buf.push(c);
                    }
                }
            } else {
                break;
            }
        }
        Err(std::io::Error::new(
            ErrorKind::InvalidData,
            format!(
                "Expected to have terminated string literal after quota {:?}, while consumed buf: {:?}",
                quote as char, buf
            ),
        ))
    }

    fn read_escaped_string_text(&mut self, buf: &mut Vec<u8>) -> Result<()> {
        loop {
            self.keep_read(buf, |f| f != b'\\');
            if self.ignore_byte(b'\\') {
                let buffer = Cursor::split(self).1;
                let c = buffer[0];
                match c {
                    b'\'' | b'\"' | b'\\' | b'/' | b'`' => {
                        buf.push(c);
                        self.consume(1);

View on GitHub (pinned to 288d84d76e)