{"record":{"id":"6f4f8ba077ab9930","repo":"dianping/cat","slug":"syntax-error-unexpected-terminals-tokenid-e","errorCode":null,"errorMessage":"syntax error, unexpected  + terminals[tokenId] + expectedString (e.g. ', expecting ' + expected.join(' or '))","messagePattern":"syntax error, unexpected  \\+ terminals\\[tokenId\\] \\+ expectedString \\(e\\.g\\. ', expecting ' \\+ expected\\.join\\(' or '\\)\\)","errorType":"validation","errorClass":"PHP.ParseError","httpStatus":null,"severity":"error","filePath":"cat-home/src/main/webapp/assets/js/editor/worker-php.js","lineNumber":2403,"sourceCode":"                            && (yyn = yybase[ state + this.YYNLSTATES] + i)\n                            && yyn < this.YYLAST && yycheck[ yyn ] == i\n                        ) {\n                            if (yyaction[ yyn ] != this.YYUNEXPECTED) {\n                                if (expected.length == 4) {\n                                    expected = [];\n                                    break;\n                                }\n\n                                expected.push( this.terminals[ i ] );\n                            }\n                        }\n                    }\n\n                    var expectedString = '';\n                    if (expected.length) {\n                        expectedString = ', expecting ' + expected.join(' or ');\n                    }\n                    throw new PHP.ParseError('syntax error, unexpected ' + terminals[ tokenId ] + expectedString, this.startAttributes['startLine']);\n                } else {\n                    return this.startAttributes['startLine'];\n                }\n\n            }\n\n            if (state < this.YYNLSTATES)\n                break;\n            yyn = state - this.YYNLSTATES;\n        }\n    }\n};\n\nPHP.ParseError = function( msg, line ) {\n    this.message = msg;\n    this.line = line;\n};\n","sourceCodeStart":2385,"sourceCodeEnd":2421,"githubUrl":"https://github.com/dianping/cat/blob/e815e74d4c2dd74edac831241f1253fcc7d25381/cat-home/src/main/webapp/assets/js/editor/worker-php.js#L2385-L2421","documentation":"PHP.ParseError thrown by the generated LALR parser tables inside the ACE PHP worker (worker-php.js, error at line 2403 in the yyerror/reporting path). It means the token sequence is grammatically invalid PHP: the parser computed the set of terminals acceptable in the current state, listed them as 'expecting ...', and reports the unexpected token it actually got, along with the start line of the offending statement.","triggerScenarios":"Any syntactically broken PHP fed to the worker: missing ';' before a statement, unbalanced braces/parens, 'foreach (array as' with missing variable, using a reserved word as a function name. The parser reaches a state where the lookahead tokenId has no action, builds the 'expected' list from this.terminals, and throws at line 2409.","commonSituations":"Live typing in the ACE PHP editor (the worker parses on each change, so half-written statements routinely produce this), pasted PHP with smart quotes, PHP 8 syntax (match, arrow fns, attributes) fed to an old parser that only knows PHP 5.x, or a file whose opening '<?php' tag is missing.","solutions":["Look at the reported start line and the 'expecting' list: insert or fix the listed punctuation/token (most often a missing semicolon, brace, or parenthesis).","If the code targets a newer PHP than the worker understands, treat the annotation as a false positive and upgrade the PHP worker/parser tables.","Check the previous line as well — LALR errors frequently surface one token after the actual mistake.","Run php -l on the file to cross-check the real PHP error position."],"exampleFix":"// before\n<?php\n$a = 1\necho $a;\n\n// after\n<?php\n$a = 1;\necho $a;","handlingStrategy":"try-catch","validationCode":"// Cheap pre-flight in the editor host before shipping PHP to the worker:\nfunction plausiblyParseable(src) {\n  if (!/^<\\?php|<\\?=/m.test(src)) return false;\n  const bal = (s, a, b) => (s.split(a).length - 1) - (s.split(b).length - 1);\n  return bal(src, '{', '}') === 0 && bal(src, '(', ')') === 0;\n}","typeGuard":"function isParseError(ex) {\n  return ex instanceof PHP.ParseError;\n}","tryCatchPattern":"try {\n  new PHP.Parser(lexer, ast).parse(src);\n} catch (ex) {\n  if (isParseError(ex)) {\n    annotate({ line: ex.line, message: ex.message }); // line lives on the error\n  } else throw ex;\n}","preventionTips":["Trust the 'expecting' list — it states exactly which token completes the construct.","Check the line BEFORE the reported one; LALR lags by a token.","Cross-check with php -l before assuming an editor false positive.","If writing PHP 8 syntax, confirm the bundled worker tables know that version."],"tags":["php","ace-editor","parser","syntax-error","lint"],"backgroundTag":null,"analyzedSha":"e815e74d4c2dd74edac831241f1253fcc7d25381","analyzedAt":"2026-08-14T14:22:34.512Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}