{"record":{"id":"e43f583aac2fc651","repo":"mozilla/pdf.js","slug":"invalid-token-in-do-end-declaration","errorCode":null,"errorMessage":"Invalid token in do ... end declaration.","messagePattern":"Invalid token in do \\.\\.\\. end declaration\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/core/xfa/formcalc_parser.js","lineNumber":1055,"sourceCode":"\n    tok = this.lexer.next();\n    if (tok.id === TOKEN.assign) {\n      const [tok1, expr] = this.parseSimpleExpr(null);\n      return [tok1, new Assignment(savedTok.value, expr)];\n    }\n\n    const parser = new SimpleExprParser(this.lexer);\n    parser.pushOperand(new AstIdentifier(savedTok.value));\n\n    return parser.parse(tok);\n  }\n\n  parseBlock() {\n    const [tok1, body] = this.parseExprList();\n\n    const tok = tok1 || this.lexer.next();\n    if (tok.id !== TOKEN.end) {\n      throw new Error(Errors.block);\n    }\n\n    return [null, new BlockDecl(body)];\n  }\n\n  parseVarDecl() {\n    // 'var' Identifier ('=' SimpleExpression)?\n    let tok = this.lexer.next();\n    if (tok.id !== TOKEN.identifier) {\n      throw new Error(Errors.var);\n    }\n\n    const identifier = tok.value;\n\n    tok = this.lexer.next();\n    if (tok.id !== TOKEN.assign) {\n      return [tok, new VarDecl(identifier, null)];\n    }","sourceCodeStart":1037,"sourceCodeEnd":1073,"githubUrl":"https://github.com/mozilla/pdf.js/blob/5903d58d58e4dd9ce6ffa3834aea8480f06b4ada/src/core/xfa/formcalc_parser.js#L1037-L1073","documentation":"Thrown by Parser.parseBlock() in the FormCalc parser when a 'do ... end' block body has been parsed but the terminator keyword 'end' (TOKEN.end) is missing. FormCalc requires every block opened with 'do' to close with 'end'; any other token at that position raises Errors.block.","triggerScenarios":"A FormCalc block where the body expression list is followed by a token that is not 'end' (e.g. 'endif', 'endfunc', EOF, or an identifier). Triggered while parsing an XFA form script containing a 'do' block.","commonSituations":"Mismatched block terminators (using 'endfor' where 'end' is expected); truncated script that ends after the body; generator emitting the wrong closer; hand-authored FormCalc confusing 'do...end' with 'for...endfor'.","solutions":["Verify that every 'do' block in the script is closed by exactly 'end'.","Check for terminator confusion: blocks use 'end', for-loops use 'endfor', while uses 'endwhile', funcs use 'endfunc', ifs use 'endif'.","Re-author the block in a FormCalc-aware tool to get correct matching closers.","If untrusted input, catch the parse failure and skip the malformed block script."],"exampleFix":"// before\ndo\n  Sum(*)\nendfor\n// after\ndo\n  Sum(*)\nend","handlingStrategy":"validation","validationCode":"// Verify each 'do' block is terminated by 'end' (simple heuristic).\nfunction blocksBalanced(src) {\n  const dos = (src.match(/\\bdo\\b/g) || []).length;\n  const ends = (src.match(/\\bend\\b/g) || []).length;\n  return dos === ends;\n}","typeGuard":null,"tryCatchPattern":"try {\n  bindFormCalcScript(xfaNode, src);\n} catch (e) {\n  if (e instanceof Error && /do \\.\\.\\. end declaration/.test(e.message)) {\n    console.warn(\"FormCalc block missing 'end'; skipping\", e.message);\n    return;\n  }\n  throw e;\n}","preventionTips":["Pair every 'do' with 'end' and do not confuse it with loop/if/func terminators.","Use a FormCalc-aware editor that auto-closes blocks.","Lint terminator pairing before binding."],"tags":["xfa","formcalc","parser","syntax","block"],"backgroundTag":null,"analyzedSha":"5903d58d58e4dd9ce6ffa3834aea8480f06b4ada","analyzedAt":"2026-08-13T02:28:27.364Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}