apple/pkl · error · GenericParserError

stringContentMustBeginOnNewLine

stringContentMustBeginOnNewLine

Error message

The content of a multi-line string must begin on a new line.

What it means

After the opening `"""` of a multi-line string, Pkl requires the content to start on its own line: the very next token must be STRING_NEWLINE. GenericParserImpl.parseMultiLineStringLiteralExpr throws this immediately after consuming the opener when the lookahead is not a newline. This rule lets Pkl strip leading indentation deterministically.

Solutions

  1. Put the string content on the line immediately after the opening `"""`.
  2. If a short string is needed on one line, use a single-line `"..."` string instead.
  3. Check code generators/templates so they emit a `\n` after the opening `"""`.

Example fix

// before
text = """hello
world"""

// after
text = """
  hello
  world
  """
Defensive patterns

Strategy: validation

Validate before calling

// Content must start on a new line after """
function multiLineStringOpensCorrectly(src) {
  const m = src.match(/"""[^\n]/);
  return !m; // true = ok
}

Prevention

When it happens

Trigger: Writing `"""content...` on the same line as the opening triple quote, e.g. `text = """hello` without a line break after `"""`.

Common situations: Porting code from languages like Kotlin without the same restriction; minifying or generating .pkl output on one line; pasting a triple-quoted string into an existing property line.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

Thrown at pkl-parser/src/main/java/org/pkl/parser/GenericParserImpl.java:1005

          ff(children);
          expect(Token.RPAREN, children, "unexpectedToken", ")");
        }
        case EOF -> {
          var delimiter = new StringBuilder(start.text(lexer)).reverse().toString();
          throw parserError("missingDelimiter", delimiter);
        }
      }
    }
    children.add(makeTerminal(next())); // string end
    return new Node(NodeType.SINGLE_LINE_STRING_LITERAL_EXPR, children);
  }

  private Node parseMultiLineStringLiteralExpr() {
    var children = new ArrayList<Node>();
    var start = next();
    children.add(makeTerminal(start)); // string start
    if (lookahead != Token.STRING_NEWLINE) {
      throw parserError(ErrorMessages.create("stringContentMustBeginOnNewLine"), spanLookahead);
    }
    while (lookahead != Token.STRING_END) {
      switch (lookahead) {
        case STRING_PART -> {
          var tk = next();
          if (!tk.text(lexer).isEmpty()) {
            children.add(make(NodeType.STRING_CHARS, tk.span));
          }
        }
        case STRING_NEWLINE -> children.add(make(NodeType.STRING_NEWLINE, next().span));
        case STRING_ESCAPE_CONTINUATION ->
            children.add(make(NodeType.STRING_CONTINUATION, next().span));
        case STRING_ESCAPE_NEWLINE,
            STRING_ESCAPE_TAB,
            STRING_ESCAPE_QUOTE,
            STRING_ESCAPE_BACKSLASH,
            STRING_ESCAPE_RETURN,
            STRING_ESCAPE_UNICODE ->

View on GitHub (pinned to f3efcbfc9b)