apple/pkl · error

missingDelimiter

missingDelimiter

Error message

Missing `{0}` delimiter.

What it means

A string literal was opened but the matching closing delimiter (a `"` preceded by the same number of `#` as the opening) was never found on the same line. Pkl single-line strings cannot contain raw newlines, so when the lexer hits a newline or carriage return inside the string body it throws missingDelimiter. The expected delimiter text includes the `#` repetition for multi-line `#"..."#` strings.

Source

Thrown at pkl-parser/src/main/java/org/pkl/parser/Lexer.java:354

    return Token.STRING_MULTI_START;
  }

  private void lexStringEnd(InterpolationScope scope) {
    // don't actually need to check it here
    for (var i = 0; i < scope.quotes + scope.pounds; i++) {
      nextChar();
    }
  }

  private void lexString(int pounds) {
    var poundsInARow = 0;
    var foundQuote = false;
    var foundBackslash = false;
    while (lookahead != EOF) {
      var ch = nextChar();
      switch (ch) {
        case '\n', '\r' ->
            throw lexError(
                ErrorMessages.create("missingDelimiter", "\"" + "#".repeat(pounds)), cursor - 1, 1);
        case '"' -> {
          if (pounds == 0) {
            backup();
            stringEnded = true;
            return;
          }
          foundQuote = true;
          foundBackslash = false;
          poundsInARow = 0;
        }
        case '\\' -> {
          foundQuote = false;
          foundBackslash = true;
          poundsInARow = 0;
          if (pounds == poundsInARow) {
            backup(pounds + 1);
            isEscape = true;

View on GitHub (pinned to f3efcbfc9b)

Solutions

  1. Add the closing `"` (with the same `#` prefix if opened as `#"`) on the same line.
  2. Use a multi-line string (`"""` form) if the value must span lines.
  3. Escape the newline with `\` line continuation or use string concatenation (`+`).
  4. Check for a stray unmatched quote earlier in the line that swallowed the real closing quote.

Example fix

// before
greeting = "hello
world"
// after
greeting = """
hello
world
"""
Defensive patterns

Strategy: validation

Validate before calling

function singleLineStringsClosed(line) { const q = (line.match(/(?<!\\)"/g) || []).length; return q % 2 === 0; }
if (!pklLines.every(singleLineStringsClosed)) throw new Error('Unbalanced quotes in single-line string');

Type guard

null

Try / catch

try { tokens = lexer.next(); } catch (e) { if (e.code === 'missingDelimiter') { console.error(`Unterminated string at line ${e.line}`); } throw e; }

Prevention

When it happens

Trigger: nextString → lexString: while scanning the body of a string, the lexer reaches a `\n` or `\r` before encountering the closing `"` (with matching `#` pounds), or encounters EOF. Any raw line break inside a single-line string literal triggers it.

Common situations: Forgetting to close a string before pressing Enter; intending a multi-line string but writing `"...` instead of `"""...` (triple-quote) form; copy-paste splitting a long string across lines without concatenation; mismatched `#` counts between opening and closing of `#"..."#`.

Related errors


AI-assisted analysis of apple/pkl@f3efcbfc9b (2026-09-08). Data as JSON: /api/errors/b649aba762870917. Report an issue: GitHub.