gastownhall/beads · error
childRef is empty after variable substitution
Error message
childRef is empty after variable substitution
What it means
generateBondedID builds a bonded molecule child ID by substituting template variables into opts.ChildRef. If substitution produces an empty string (e.g. the childRef was entirely a variable like '{{name}}' and that variable resolved to empty), it throws 'childRef is empty after variable substitution'. The guard exists because an empty childRef would yield malformed IDs like 'parent.'.
Source
Thrown at cmd/bd/template.go:513
// generateBondedID creates a custom ID for dynamically bonded molecules.
// When bonding a proto to a parent molecule, this generates IDs like:
// - Root: parent.childref (e.g., "patrol-x7k.arm-ace")
// - Children: parent.childref.step (e.g., "patrol-x7k.arm-ace.capture")
//
// The childRef is variable-substituted before use.
// Returns empty string if not a bonded operation (opts.ParentID empty).
func generateBondedID(oldID string, rootID string, opts CloneOptions) (string, error) {
if opts.ParentID == "" {
return "", nil // Not a bonded operation
}
// Substitute variables in childRef
childRef := substituteVariables(opts.ChildRef, opts.Vars)
// Validate childRef after substitution
if childRef == "" {
return "", fmt.Errorf("childRef is empty after variable substitution")
}
if !bondedIDPattern.MatchString(childRef) {
return "", fmt.Errorf("invalid childRef '%s': must be alphanumeric, dash, underscore, or dot only", childRef)
}
if oldID == rootID {
// Root issue: parent.childref
newID := fmt.Sprintf("%s.%s", opts.ParentID, childRef)
return newID, nil
}
// Child issue: parent.childref.relative
// Extract the relative portion of the old ID (part after root)
relativeID := getRelativeID(oldID, rootID)
if relativeID == "" {
// No hierarchical relationship - use a suffix from the old ID to ensure uniqueness.
// Extract the last part of the old ID (after any prefix or dash)
suffix := extractIDSuffix(oldID)View on GitHub (pinned to 71377f2769)
Solutions
- Pass a non-empty value for the variable used in ChildRef in CloneOptions.Vars.
- Set a literal (non-variable) ChildRef if no dynamic naming is needed.
- Check the proto template for which variables it expects in childRef and provide them all.
- If a variable may be empty, give ChildRef a static prefix, e.g. 'task-{{n}}' instead of '{{n}}'.
Example fix
// before
opts := CloneOptions{ChildRef: "{{suffix}}", Vars: map[string]string{}}
// after
opts := CloneOptions{ChildRef: "{{suffix}}", Vars: map[string]string{"suffix": "step1"}} Defensive patterns
Strategy: validation
Validate before calling
resolved := substituteVariables(opts.ChildRef, opts.Vars)
if resolved == "" {
return fmt.Errorf("childRef resolves to empty; provide vars for %s", opts.ChildRef)
} Try / catch
newID, err := generateBondedID(ctx, tx, oldID, rootID, opts)
if err != nil && strings.Contains(err.Error(), "childRef is empty") {
return fmt.Errorf("provide a value for the childRef variable: %w", err)
} Prevention
- Ensure every variable used in ChildRef has a non-empty entry in CloneOptions.Vars.
- Prefer mixed literal+variable childRefs ('task-{{n}}') over pure variables.
- Validate Vars keys against the template's extractVariables output before cloning.
When it happens
Trigger: Cloning/bonding a subgraph (cloneSubgraphInto or direct call) where CloneOptions.ChildRef consists only of variables that resolve to empty strings in opts.Vars — e.g. ChildRef='{{suffix}}' with Vars lacking 'suffix' or having an empty value.
Common situations: Template variables renamed in the proto but the caller still passes old variable names in Vars; an empty CLI flag value substituted in; Handlebars-style {{var}} left unresolved and stripped to empty.
Related errors
- invalid childRef '%s': must be alphanumeric, dash, underscor
- runtime mode requires all variables to have values Missing:
- formula %q: %w
- missing required variables: %s
- ErrVarValidation
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/6e24195f68dd7043.
Report an issue: GitHub.