apple/pkl · error · ParserError

stringContentMustBeginOnNewLine

stringContentMustBeginOnNewLine

Error message

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

What it means

In a Pkl multi-line string ("""..."""), the content is required to start on its own line after the opening delimiter. The parser validates the parsed string nodes and throws when the very first node is not a newline token, i.e. content immediately follows the opening triple quote. This rule keeps multi-line strings free of the opening delimiter's indentation.

Source

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

        case EOF -> {
          var delimiter = new StringBuilder(start.text(lexer)).reverse().toString();
          throw parserError("missingDelimiter", delimiter);
        }
      }
    }
    var end = next().span;
    var fullSpan = start.span.endWith(end);
    var parts = validateMultiLineString(stringTokens, fullSpan);
    return new MultiLineStringLiteralExpr(parts, start.span, end, fullSpan);
  }

  private List<StringPart> validateMultiLineString(List<TempNode> nodes, Span span) {
    var firstNode = nodes.isEmpty() ? null : nodes.get(0);
    if (firstNode == null
        || firstNode.token == null
        || firstNode.token.token != Token.STRING_NEWLINE) {
      var errorSpan = firstNode == null ? span : firstNode.span();
      throw new ParserError(ErrorMessages.create("stringContentMustBeginOnNewLine"), errorSpan);
    }
    // only contains a newline
    if (nodes.size() == 1) {
      return List.of(new StringChars("", firstNode.span()));
    }
    var indent = getCommonIndent(nodes, span);
    return renderString(nodes, indent);
  }

  private List<StringPart> renderString(List<TempNode> nodes, String commonIndent) {
    var parts = new ArrayList<StringPart>();
    var builder = new StringBuilder();
    var lastToken = nodes.get(nodes.size() - 1).token;
    assert lastToken != null;
    var endOffset = lastToken.token == Token.STRING_NEWLINE ? 1 : 2;
    var isNewLine = true;
    Span start = null;
    Span end = null;

View on GitHub (pinned to f3efcbfc9b)

Solutions

  1. Put a line break immediately after the opening `"""` delimiter so content starts on the next line
  2. Indent the string content; Pkl strips the common indentation automatically
  3. If a single-line value was intended, use a normal quoted string instead of a multi-line one

Example fix

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

Strategy: validation

Validate before calling

function multilineStringOpensOnNewLine(line) {
  const m = line.match(/"""(.*)$/);
  return !(m && m[1].trim() !== ''); // true if opening """ is alone on its line
}
// scan each line of the .pkl source containing '"""' before parsing

Type guard

null

Try / catch

try {
  const result = parser.parse(source);
} catch (e) {
  if (e.errorId === 'stringContentMustBeginOnNewLine') {
    // suggest inserting a newline after the opening triple quote at e.span
  } else { throw e; }
}

Prevention

When it happens

Trigger: Parsing source where a multi-line string is opened like `text: """hello` with content on the same line as the opening `"""`, or where the string is empty/malformed such that no STRING_NEWLINE token starts the content list.

Common situations: Developers used to Kotlin/Scala raw strings writing `"""text` on one line; converting YAML or heredocs to Pkl; editor snippets that collapse the newline after the opening delimiter.

Related errors


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