databendlabs/databend · error

Expected to have terminated string literal after escaped…

Error message

Expected to have terminated string literal after escaped char '\' .

What it means

Thrown by `CursorReadStringExt::read_quoted_text` when it encounters a backslash escape character but the cursor is exhausted immediately after it — i.e. the input ends with a dangling `\` and no character follows to unescape. The parser requires an escape sequence to be complete inside a quoted string literal.

Solutions

  1. Escape the trailing backslash by doubling it: `C:\\` instead of `C:\`.
  2. Check the source string for a trailing '\' and append a second backslash before parsing.
  3. If the input comes from an external system, verify it was not truncated before the closing quote.

Example fix

// before
let input = r#""C:\""#; // dangling escape
// after
let input = r#""C:\\""#; // escaped backslash
Defensive patterns

Strategy: validation

Validate before calling

fn has_dangling_escape(s: &str) -> bool {
    s.chars().rev().take_while(|c| *c == '\\').count() % 2 == 1
}

Type guard

fn ends_with_complete_escape(s: &str) -> bool {
    !has_dangling_escape(s)
}

Try / catch

match read_quoted_text(&mut buf) {
    Ok(s) => s,
    Err(e) if e.to_string().contains("escaped char") => {
        // input ended with a lone backslash; fix or reject
        Err(e)
    }
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: Calling `read_quoted_text` on a quoted literal whose content ends with a trailing backslash, e.g. `"abc\` (quote opened, backslash consumed, then end of buffer).

Common situations: User-provided string literals (SQL values, JSON-ish text, config entries) with an unescaped trailing backslash from a Windows path (C:\), a truncated write, or an escaping pass that doubled up incorrectly.

Related errors


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

Appendix: source

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

impl<T> BufferReadStringExt for Cursor<T>
where T: AsRef<[u8]>
{
    fn read_quoted_text(&mut self, buf: &mut Vec<u8>, quote: u8) -> Result<()> {
        self.must_ignore_byte(quote)?;

        loop {
            self.keep_read(buf, |b| b != quote && b != b'\\');
            if self.ignore_byte(quote) {
                if self.peek_byte() == Some(quote) {
                    buf.push(quote);
                    self.consume(1);
                } else {
                    return Ok(());
                }
            } else if self.ignore_byte(b'\\') {
                let b = Cursor::split(self).1;
                if b.is_empty() {
                    return Err(std::io::Error::new(
                        ErrorKind::InvalidData,
                        "Expected to have terminated string literal after escaped char '\' ."
                            .to_string(),
                    ));
                }
                let c = b[0];
                self.ignore_byte(c);

                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'/'),
                    _ => {

View on GitHub (pinned to 288d84d76e)