{"record":{"id":"c3bed39793bfef5b","repo":"apple/pkl","slug":"stringcontentmustbeginonnewline-c3bed3","errorCode":"stringContentMustBeginOnNewLine","errorMessage":"The content of a multi-line string must begin on a new line.","messagePattern":"The content of a multi-line string must begin on a new line\\.","errorType":"exception","errorClass":"GenericParserError","httpStatus":null,"severity":"error","filePath":"pkl-parser/src/main/java/org/pkl/parser/GenericParserImpl.java","lineNumber":1005,"sourceCode":"          ff(children);\n          expect(Token.RPAREN, children, \"unexpectedToken\", \")\");\n        }\n        case EOF -> {\n          var delimiter = new StringBuilder(start.text(lexer)).reverse().toString();\n          throw parserError(\"missingDelimiter\", delimiter);\n        }\n      }\n    }\n    children.add(makeTerminal(next())); // string end\n    return new Node(NodeType.SINGLE_LINE_STRING_LITERAL_EXPR, children);\n  }\n\n  private Node parseMultiLineStringLiteralExpr() {\n    var children = new ArrayList<Node>();\n    var start = next();\n    children.add(makeTerminal(start)); // string start\n    if (lookahead != Token.STRING_NEWLINE) {\n      throw parserError(ErrorMessages.create(\"stringContentMustBeginOnNewLine\"), spanLookahead);\n    }\n    while (lookahead != Token.STRING_END) {\n      switch (lookahead) {\n        case STRING_PART -> {\n          var tk = next();\n          if (!tk.text(lexer).isEmpty()) {\n            children.add(make(NodeType.STRING_CHARS, tk.span));\n          }\n        }\n        case STRING_NEWLINE -> children.add(make(NodeType.STRING_NEWLINE, next().span));\n        case STRING_ESCAPE_CONTINUATION ->\n            children.add(make(NodeType.STRING_CONTINUATION, next().span));\n        case STRING_ESCAPE_NEWLINE,\n            STRING_ESCAPE_TAB,\n            STRING_ESCAPE_QUOTE,\n            STRING_ESCAPE_BACKSLASH,\n            STRING_ESCAPE_RETURN,\n            STRING_ESCAPE_UNICODE ->","sourceCodeStart":987,"sourceCodeEnd":1023,"githubUrl":"https://github.com/apple/pkl/blob/f3efcbfc9b60d30053b0536d664948d7aa1b8673/pkl-parser/src/main/java/org/pkl/parser/GenericParserImpl.java#L987-L1023","documentation":"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.","triggerScenarios":"Writing `\"\"\"content...` on the same line as the opening triple quote, e.g. `text = \"\"\"hello` without a line break after `\"\"\"`.","commonSituations":"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.","solutions":["Put the string content on the line immediately after the opening `\"\"\"`.","If a short string is needed on one line, use a single-line `\"...\"` string instead.","Check code generators/templates so they emit a `\\n` after the opening `\"\"\"`."],"exampleFix":"// before\ntext = \"\"\"hello\nworld\"\"\"\n\n// after\ntext = \"\"\"\n  hello\n  world\n  \"\"\"","handlingStrategy":"validation","validationCode":"// Content must start on a new line after \"\"\"\nfunction multiLineStringOpensCorrectly(src) {\n  const m = src.match(/\"\"\"[^\\n]/);\n  return !m; // true = ok\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always write multi-line strings as `\"\"\"\\n ... \\n  \"\"\"` with the opener alone on its line.","Configure formatters/templates to emit a newline after the opening delimiter.","Never minify multi-line string blocks."],"tags":["pkl","parser","syntax-error","multiline-string"],"backgroundTag":"invalid-argument-format","analyzedSha":"f3efcbfc9b60d30053b0536d664948d7aa1b8673","analyzedAt":"2026-09-08T13:10:45.570Z","contentChangedAt":"2026-09-08T13:10:45.570Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}