hcengineering/platform · error
Variable ${matched[0]} not found
Error message
Variable ${matched[0]} not found What it means
fillValue substitutes variables of the form matched by fieldRegexp (e.g. {{name}}-style placeholders) from a vars lookup map. When a placeholder is found in the string but has no entry in vars, the value cannot be resolved, so it throws naming the missing variable. The loop repeats until no placeholders remain, so any unresolved variable aborts the fill.
Source
Thrown at server/tool/src/initializer.ts:386
if (Array.isArray(value)) {
return await Promise.all(value.map(async (v) => await this.fillValue(v, vars)))
} else {
return await this.fillProps(value, vars)
}
} else if (typeof value === 'string') {
if (value === this.nextRank) {
const rank = makeRank(vars[this.nextRank], undefined)
vars[this.nextRank] = rank
return rank
} else if (value === this.now) {
return new Date().getTime()
} else {
while (true) {
const matched = fieldRegexp.exec(value)
if (matched === null) break
const result = vars[matched[0]]
if (result === undefined) {
throw new Error(`Variable ${matched[0]} not found`)
} else {
value = value.replaceAll(matched[0], result)
fieldRegexp.lastIndex = 0
}
}
return value
}
}
return value
}
}
View on GitHub (pinned to 63e28dc964)
Solutions
- Add the missing variable to the vars map with the exact key the regexp matches (including delimiters if matched[0] includes them)
- Print the vars object and compare keys against the placeholders in the value
- Fix the template typo in the property value
Example fix
// before
const value = 'Hello {{customerName}}' // vars has only { name: 'John' }
// after
const vars = { ...baseVars, '{{customerName}}': customer.name } // key must match matched[0]
// or fix the template to use an existing variable: 'Hello {{name}}' Defensive patterns
Strategy: validation
Validate before calling
function findUnresolvedVars(value: string, vars: Record<string, string>, regexp: RegExp): string[] {
const missing: string[] = []
for (const m of value.match(new RegExp(regexp.source, regexp.flags.replace('g', '') + 'g')) ?? []) {
if (vars[m] === undefined) missing.push(m)
}
return missing
}
const missing = findUnresolvedVars(template, vars, fieldRegexp)
if (missing.length > 0) throw new Error(`Unresolved variables: ${missing.join(', ')}`) Try / catch
try {
filled = await fillValue(value, vars)
} catch (err) {
if (err instanceof Error && err.message.includes('not found')) {
const varName = err.message.match(/Variable (.+) not found/)?.[1]
console.error(`Add '${varName}' to vars or fix the template`, { available: Object.keys(vars) })
}
throw err
} Prevention
- Keep variable keys consistent between the vars map and template placeholders (mind delimiter characters)
- Pre-validate templates by extracting all placeholders and diffing against Object.keys(vars)
- Centralize variable definitions so renames propagate to all templates
When it happens
Trigger: fillProps/fillValue encountering a placeholder in a property value whose key (matched[0], including delimiters) is absent from the vars map — e.g. variable defined with a different key spelling, or vars not populated for this step.
Common situations: Migration templates referencing a variable that was never registered; variables keyed without the regexp delimiters in the map while the lookup uses matched[0] (with delimiters); renamed variables in templates without updating the vars source (config/account data).
Related errors
AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29).
Data as JSON: /api/errors/10e433467371ad26.
Report an issue: GitHub.