apple/pkl · error · ParserError

stringIndentationMustMatchLastLine

stringIndentationMustMatchLastLine

Error message

Line must match or exceed indentation of the String's last line.

What it means

Pkl multi-line strings determine their indentation from the line containing the closing delimiter, and every subsequent content line must be indented at least as far as that common indent. When a text line following a newline starts with less whitespace than the computed common indent, the parser removes the indent and throws, highlighting just the offending line's actual indent span.

Source

Thrown at pkl-parser/src/main/java/org/pkl/parser/ParserImpl.java:1251

        if (start == null) {
          start = token.span;
        }
        end = token.span;
        switch (token.token) {
          case STRING_NEWLINE -> {
            builder.append('\n');
            isNewLine = true;
          }
          case STRING_ESCAPE_CONTINUATION -> isNewLine = true;
          case STRING_PART -> {
            var text = token.text(lexer);
            if (isNewLine) {
              if (text.startsWith(commonIndent)) {
                builder.append(text, commonIndent.length(), text.length());
              } else {
                var actualIndent = getLeadingIndentCount(text);
                var textSpan = token.span.move(actualIndent).grow(-actualIndent);
                throw new ParserError(
                    ErrorMessages.create("stringIndentationMustMatchLastLine"), textSpan);
              }
            } else {
              builder.append(text);
            }
            isNewLine = false;
          }
          default -> {
            if (isNewLine && !commonIndent.isEmpty()) {
              throw new ParserError(
                  ErrorMessages.create("stringIndentationMustMatchLastLine"), token.span);
            }
            builder.append(getEscapeText(token));
            isNewLine = false;
          }
        }
      }
    }

View on GitHub (pinned to f3efcbfc9b)

Solutions

  1. Re-indent the flagged line so it has at least the same leading whitespace as the closing `"""` line
  2. Convert all indentation in the string to the same character type (spaces vs tabs)
  3. Normalize the whole block's indentation so every content line aligns with or exceeds the closing delimiter's indent
  4. Use an editor Pkl plugin / formatter to fix multi-line string indentation automatically

Example fix

// before
msg: """
    hello
  world
    """
// after
msg: """
    hello
    world
    """
Defensive patterns

Strategy: validation

Validate before calling

function checkMultilineIndent(block) {
  const lines = block.split('\n');
  const closer = lines[lines.length - 1];
  const indent = closer.match(/^\s*/)[0];
  return lines.slice(1, -1).every(l => l.trim() === '' || l.startsWith(indent));
}
// verify every content line is indented at least as far as the closing """ line

Type guard

null

Try / catch

try {
  const result = parser.parse(source);
} catch (e) {
  if (e.errorId === 'stringIndentationMustMatchLastLine') {
    // re-indent the flagged line to match the closing delimiter indent
  } else { throw e; }
}

Prevention

When it happens

Trigger: Parsing a multi-line string where an inner line has less leading whitespace than the closing-delimiter line, e.g. content indented 2 spaces while the `"""` closer is indented 4 spaces, so the line's prefix doesn't start with commonIndent.

Common situations: Mixed tabs and spaces making one line appear shorter; an editor auto-dedenting a continuation line; hand-flattening a Pkl snippet and de-indenting the last content line above the closer.

Related errors


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