{"record":{"id":"38d7dbca9d36f071","repo":"emberjs/ember.js","slug":"node-path-type-node-path-type-stringlit","errorCode":null,"errorMessage":"${node.path.type} \"${node.path.type === 'StringLiteral' ? node.path.original : value}\" cannot be called as a sub-expression, replace (${value}) with ${value}","messagePattern":"(.+?) \"(.+?)\" cannot be called as a sub-expression, replace \\((.+?)\\) with (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/@glimmer/syntax/lib/parser/handlebars-node-visitors.ts","lineNumber":635,"sourceCode":"\n    case 'StringLiteral':\n    case 'UndefinedLiteral':\n    case 'NullLiteral':\n    case 'NumberLiteral':\n    case 'BooleanLiteral': {\n      let value: string;\n      if (node.path.type === 'BooleanLiteral') {\n        value = node.path.original.toString();\n      } else if (node.path.type === 'StringLiteral') {\n        value = `\"${node.path.original}\"`;\n      } else if (node.path.type === 'NullLiteral') {\n        value = 'null';\n      } else if (node.path.type === 'NumberLiteral') {\n        value = node.path.value.toString();\n      } else {\n        value = 'undefined';\n      }\n      throw generateSyntaxError(\n        `${node.path.type} \"${\n          node.path.type === 'StringLiteral' ? node.path.original : value\n        }\" cannot be called as a sub-expression, replace (${value}) with ${value}`,\n        compiler.source.spanFor(node.path.loc)\n      );\n    }\n  }\n\n  const params = node.params.map((e) => compiler.acceptNode<HBS.Expression['type']>(e));\n\n  // if there is no hash, position it as a collapsed node immediately after the last param (or the\n  // path, if there are also no params)\n  const end = isPresentArray(params) ? getLast(params).loc : path.loc;\n\n  const hash = node.hash\n    ? compiler.Hash(node.hash)\n    : b.hash({\n        pairs: [],","sourceCodeStart":617,"sourceCodeEnd":653,"githubUrl":"https://github.com/emberjs/ember.js/blob/26f97246a8bf2e28edf26ac3093da2e86c04ffc5/packages/@glimmer/syntax/lib/parser/handlebars-node-visitors.ts#L617-L653","documentation":"In a call/sub-expression position like {{value}} the path must be an identifier-based path, not a Handlebars literal. When node.path is a StringLiteral, NumberLiteral, BooleanLiteral, UndefinedLiteral or NullLiteral, the parser cannot treat the literal as a callable path and throws, suggesting to drop the parentheses (i.e. use the literal directly, not as a call).","triggerScenarios":"Invoking the parser with a mustache or sub-expression whose path is a literal — e.g. {{(\"foo\")}}, {{(42)}}, {{(true)}} — or passing {{123}} where the visitor's acceptCallNodes (called with {path, params, hash}) detects isHBSLiteral(node.path). The message interpolates the literal's type and value.","commonSituations":"Programmatic template generation that wraps values in parentheses assuming expression syntax; confused template syntax where a literal was meant to be passed as a param but got placed in the path slot; copy/paste from languages where (\"str\") is a valid expression.","solutions":["Remove the parentheses and use the literal directly: (\"foo\") → \"foo\", (42) → 42, (true) → true.","If you meant to call a helper, put an identifier path in the call position and pass literals as arguments: {{uppercase \"foo\"}} instead of {{(\"foo\")}}.","Fix the generator/macro that places literals in the path position of call nodes."],"exampleFix":"// before\n{{(\"hello\")}}\n\n// after\n{{\"hello\"}}\n// or, if a helper call was intended:\n{{uppercase \"hello\"}}","handlingStrategy":"validation","validationCode":"// reject literal paths inside parenthesized sub-expressions\nfunction assertNoLiteralCalls(template) {\n  const bad = template.match(/\\(\\s*(\"[^\"]*\"|'[^']*'|\\d+|true|false|null|undefined)\\s*\\)/g);\n  if (bad) throw new Error(`Literals cannot be called as sub-expressions: ${bad.join(', ')}`);\n}\nassertNoLiteralCalls(template);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use literals directly ({{\"text\"}}, {{42}}) — never wrapped in parentheses","Place literals as helper arguments, not in the call path position: {{helper \"arg\"}}","Ensure code generators emit identifier paths for call nodes","Add a precompile/lint step to CI"],"tags":["glimmer","handlebars","template-syntax","subexpression","literals"],"backgroundTag":"glimmer-literal-as-subexpression","analyzedSha":"26f97246a8bf2e28edf26ac3093da2e86c04ffc5","analyzedAt":"2026-09-01T05:01:28.182Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}