karatelabs/karate · error · MarkupHintException
ReferenceError: ' ' is not defined — did you mean `_. `?…
Error message
ReferenceError: '${missing}' is not defined — did you mean `_.${missing}`? ka:scope blocks namespace template state via the `_` map; ... What it means
When JS evaluation in a markup template fails with a ReferenceError for an unknown name, karate-core checks whether the name exists in the template vars and produces a MarkupHintException suggesting `_.<name>`. ka:scope blocks namespace template state via the `_` map, so a variable set in scope is accessed as `_.name`, not as a bare global.
Solutions
- Prefix the variable with `_.` as the hint suggests: `_.count` instead of `count`
- Verify the exact variable name spelling against what was set via context.set() or `_.x =`
- If the value should be a plain template variable, define it in the template model instead of the scope map
- Check whether the code was moved into/out of a ka:scope block, which changes the namespace
Example fix
// before
<span>${count}</span>
// after
<span>${_.count}</span> Defensive patterns
Strategy: type-guard
Validate before calling
// check the name exists in the underscore scope before use if (!_.hasOwnProperty(name)) throw new Error(name + " not in _ scope; set it via context.set");
Type guard
function scopeRef(name) { return typeof _[name] !== 'undefined' ? _[name] : undefined; } Try / catch
try { evalTemplate(expr); } catch (MarkupHintException e) { if (e.getMessage().contains("is not defined")) { log.error("scope hint: " + e.getMessage()); } else throw e; } Prevention
- Prefix scope-stored variables with `_` inside ka:scope blocks
- Verify spelling against what context.set() stored
- Keep scope variables documented per template to avoid namespace confusion
When it happens
Trigger: Referencing a bare identifier inside a template (or inside a ka:scope block) that was stored via context.set() / `_.x = value`, e.g. writing `count` instead of `_.count`; typo'd variable names; variables that only exist in localVars but were shadowed.
Common situations: Authors forgetting the `_` namespace prefix after moving code into a ka:scope block; migration from older templates where variables were plain globals; typos in template variable names.
Related errors
- context.set() requires a name argument
- ${hint: names offending unquoted object-literal keys…
- cannot access ' ' before initialization
- is not defined
- is not defined
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/a6ca51a058786f5a.
Report an issue: GitHub.
Appendix: source
Thrown at karate-core/src/main/java/io/karatelabs/markup/MarkupTemplateContext.java:282
Map<String, Object> localVars = new HashMap<>();
for (String name : getVariableNames()) {
localVars.put(name, getVariable(name));
}
// Bind `_` as the dual-lookup ObjectLike (not the raw vars Map)
// so template-attribute reads of `_.<name>` fall through to the
// wrapped Thymeleaf scope when the underscore map is empty.
localVars.put("_", underscoreView);
// Strict ReferenceError on missing names — augment with an actionable
// hint pointing at either the `_.<name>` discipline or the
// th:with-at-call-site / context.get(...) optional-param pattern.
try {
return engine.evalWith(src, localVars);
} catch (io.karatelabs.js.EngineException e) {
String missing = extractMissingName(e);
if (missing == null) {
throw e;
}
throw new MarkupHintException(buildMissingNameHint(missing, e), e);
}
}
private String buildMissingNameHint(String missing, Throwable cause) {
String base = "ReferenceError: '" + missing + "' is not defined";
if (vars.containsKey(missing)) {
return base + " — did you mean `_." + missing + "`? "
+ "ka:scope blocks namespace template state via the `_` map; "
+ "bare names must come from a th:with or a parent template binding.";
}
return base + " — if `" + missing + "` is a fragment parameter, "
+ "declare it via th:with at the call site "
+ "(e.g. <div th:insert=\"~{file::frag}\" th:with=\"" + missing + ": value\">). "
+ "If it's optional, read it inside the fragment via "
+ "context.get('" + missing + "') or context.get('" + missing + "', defaultValue) — "
+ "returns null (or the default) when the name is unbound.";
}
View on GitHub (pinned to a22eb90246)