quasarframework/quasar · error · Error

${message} at line ${lineNo} col ${colNo} (unclosed comment/

Error message

${message} at line ${lineNo} col ${colNo} (unclosed comment/unclosed string/unclosed tag)

What it means

Quasar's HTML template preprocessor parses the index.html template into an AST; when the htmlparser2 tokenizer fails it reports the failure as a generic parse error annotated with the line and column plus the likely causes (unclosed comment, unclosed string, unclosed tag). throwParseError also prints the offending source line with a caret for context.

Source

Thrown at app-vite/lib/utils/template.js:44

function throwParseError(message, str, index) {
  const whitespace = str.slice(0, index).split(newlineRE)

  const lineNo = whitespace.length
  const colNo = whitespace[lineNo - 1].length + 1

  message +=
    ' at line ' +
    lineNo +
    ' col ' +
    colNo +
    ':\n\n' +
    '  ' +
    str.split(newlineRE)[lineNo - 1] +
    '\n' +
    ' '.repeat(colNo + 1) +
    '^'

  throw new Error(message)
}

function escapeRegExp(str) {
  // From MDN
  return str.replace(escapeRegexpRE, String.raw`\$&`) // $& means the whole matched string
}

function trimWS(str, wsLeft, wsRight) {
  // Slightly confusing,
  // but _}} will trim the left side of the following string
  const leftTrim = wsLeft || wsLeft === false ? wsLeft : 'nl'
  const rightTrim = wsRight || wsRight === false ? wsRight : false

  if (!rightTrim && !leftTrim) return str

  if (leftTrim === 'slurp' && rightTrim === 'slurp') {
    return str.trim()
  }

View on GitHub (pinned to 4841521b5f)

Solutions

  1. Open the file and go to the reported line/column; fix the unclosed comment, quote or tag
  2. Look one or two lines above the caret — the actual mistake often starts earlier
  3. Validate the template with an HTML validator or by opening it in a browser's devtools parser
  4. Check for leftover Git conflict markers (<<<<<<<, =======) in index.html

Example fix

// before (index.html)
<!-- build configuration
<title>App</title>
// after
<!-- build configuration -->
<title>App</title>
Defensive patterns

Strategy: try-catch

Validate before calling

// lightweight sanity check before build-time template parsing
const html = fs.readFileSync('index.html', 'utf8')
if ((html.match(/<!--/g) || []).length !== (html.match(/-->/g) || []).length) {
  throw new Error('index.html has an unclosed HTML comment')
}

Try / catch

try {
  const ast = getAST(templateHtml)
} catch (err) {
  if (err.message.includes('unclosed comment/unclosed string/unclosed tag')) {
    console.error(err.message) // includes line/col + caret context
    process.exit(1)
  } else throw err
}

Prevention

When it happens

Trigger: An index.html (or custom app template) passed to getAST contains malformed markup: an unterminated <!-- comment, an attribute string never closed, or a tag missing its closing '>' / matching end tag.

Common situations: Hand-editing index.html and leaving '<!-- TODO' open; a copy/paste that dropped a closing quote in an attribute; a build-time template injection inserting raw '<' into the head; merge conflicts leaving stray conflict markers in the template.


AI-assisted analysis of quasarframework/quasar@4841521b5f (2026-08-30). Data as JSON: /api/errors/5090745f8dac2082. Report an issue: GitHub.