{"record":{"id":"4b60fd4278b098df","repo":"GoogleChrome/lighthouse","slug":"should-only-be-used-at-the-end-of-the-pattern","errorCode":null,"errorMessage":"\"$\" should only be used at the end of the pattern","messagePattern":"\"\\$\" should only be used at the end of the pattern","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"core/audits/seo/robots-txt.js","lineNumber":100,"sourceCode":"\n    if (!SITEMAP_VALID_PROTOCOLS.has(sitemapUrl.protocol)) {\n      throw new Error('Invalid sitemap URL protocol');\n    }\n  }\n\n  if (directiveName === DIRECTIVE_USER_AGENT && !directiveValue) {\n    throw new Error('No user-agent specified');\n  }\n\n  if (directiveName === DIRECTIVE_ALLOW || directiveName === DIRECTIVE_DISALLOW) {\n    if (directiveValue !== '' && directiveValue[0] !== '/' && directiveValue[0] !== '*') {\n      throw new Error('Pattern should either be empty, start with \"/\" or \"*\"');\n    }\n\n    const dollarIndex = directiveValue.indexOf('$');\n\n    if (dollarIndex !== -1 && dollarIndex !== directiveValue.length - 1) {\n      throw new Error('\"$\" should only be used at the end of the pattern');\n    }\n  }\n}\n\n/**\n * @param {string} line single line from a robots.txt file\n * @throws will throw an exception if given line has errors\n * @return {{directive: string, value: string}|null}\n */\nfunction parseLine(line) {\n  const hashIndex = line.indexOf('#');\n\n  if (hashIndex !== -1) {\n    line = line.substr(0, hashIndex);\n  }\n\n  line = line.trim();\n","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/GoogleChrome/lighthouse/blob/9515cd4e58ebed69f78742d932b501c2cab8ad8f/core/audits/seo/robots-txt.js#L82-L118","documentation":"Thrown by verifyDirective() when a '$' end-of-pattern anchor character appears anywhere except the last position of an Allow or Disallow value. In robots.txt, '$' is a supported extension meaning 'end of URL path' — it is only meaningful and valid at the end. A '$' in the middle is a syntax error that crawlers would misinterpret.","triggerScenarios":"A robots.txt line like 'Disallow: /foo$bar' where '$' appears mid-pattern. The check verifies `dollarIndex !== -1 && dollarIndex !== directiveValue.length - 1`, so any '$' that is not the final character triggers the throw.","commonSituations":"Developer treats '$' as a regex variable or literal character rather than a robots.txt end-anchor. Copying regex patterns verbatim into robots.txt. A URL containing literal '$' characters that the developer tried to match without escaping.","solutions":["Move '$' to the end of the pattern, e.g. 'Disallow: /foo$' to match paths ending in /foo","If the literal '$' is needed in a path, use '*' wildcard matching instead since '$' cannot appear mid-pattern","Remove the '$' entirely if no end-anchoring is needed"],"exampleFix":"// before\nDisallow: /download$file\n\n// after\nDisallow: /download*","handlingStrategy":"validation","validationCode":"// Validate that '$' only appears at the end of patterns\nfunction validateDollarAnchor(content) {\n  const lines = content.split(/\\r\\n|\\r|\\n/);\n  const errors = [];\n  lines.forEach((line, idx) => {\n    const trimmed = line.split('#')[0].trim();\n    const match = /^(allow|disallow)\\s*:\\s*(.*)$/i.exec(trimmed);\n    if (match) {\n      const value = match[2];\n      const dollarIdx = value.indexOf('$');\n      if (dollarIdx !== -1 && dollarIdx !== value.length - 1) {\n        errors.push(`Line ${idx + 1}: '$' must be at the end of the pattern`);\n      }\n    }\n  });\n  return errors;\n}","typeGuard":null,"tryCatchPattern":"try {\n  parseLine(line);\n} catch (e) {\n  // e.message === '\"$\" should only be used at the end of the pattern'\n  robotsErrors.push({ line, message: e.message });\n}","preventionTips":["Only use '$' as the last character in Allow/Disallow patterns","Remember '$' means end-of-URL-path in robots.txt, not a variable","Run a robots.txt validator before deploying changes"],"tags":["robots-txt","seo","validation","parsing"],"backgroundTag":null,"analyzedSha":"9515cd4e58ebed69f78742d932b501c2cab8ad8f","analyzedAt":"2026-08-13T06:28:10.346Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}