gastownhall/beads · error
formula validation failed: - %s
Error message
formula validation failed: - %s
What it means
The formula Validate method aggregates all structural problems it finds (missing IDs, missing compose hooks' attach fields, unknown references, etc.) into a single error listing each problem prefixed with " - ". It is the top-level validation failure for a formula document, not a single specific defect.
Source
Thrown at internal/formula/types.go:677
if bp.BeforeStep != "" {
if _, exists := stepIDLocations[bp.BeforeStep]; !exists {
errs = append(errs, fmt.Sprintf("compose.bond_points[%d] (%s): before_step references unknown step %q", i, bp.ID, bp.BeforeStep))
}
}
}
for i, hook := range f.Compose.Hooks {
if hook.Trigger == "" {
errs = append(errs, fmt.Sprintf("compose.hooks[%d]: trigger is required", i))
}
if hook.Attach == "" {
errs = append(errs, fmt.Sprintf("compose.hooks[%d]: attach is required", i))
}
}
}
if len(errs) > 0 {
return fmt.Errorf("formula validation failed:\n - %s", strings.Join(errs, "\n - "))
}
return nil
}
// collectChildIDs recursively collects step IDs from children.
// idLocations maps ID -> location where first defined (for better duplicate error messages).
func collectChildIDs(children []*Step, idLocations map[string]string, errs *[]string, prefix string) {
for i, child := range children {
childPrefix := fmt.Sprintf("%s.children[%d]", prefix, i)
if child.ID == "" {
*errs = append(*errs, fmt.Sprintf("%s: id is required", childPrefix))
continue
}
if firstLoc, exists := idLocations[child.ID]; exists {
*errs = append(*errs, fmt.Sprintf("%s: duplicate id %q (first defined at %s)", childPrefix, child.ID, firstLoc))
} else {
idLocations[child.ID] = childPrefixView on GitHub (pinned to 71377f2769)
Solutions
- Read each bullet in the error message; each names the offending path (e.g. compose.hooks[0]).
- Fix the indicated fields — e.g. add `attach` to each compose hook.
- Re-run validation until the error disappears; the list regenerates fresh each time.
- Compare against a known-good example formula for the current schema.
Example fix
// before (TOML) [[compose.hooks]] command = "setup" // after [[compose.hooks]] command = "setup" attach = "step-1"
Defensive patterns
Strategy: validation
Validate before calling
if err := formula.Validate(); err != nil {
// err lists every problem, one per line prefixed by " - "
lines := strings.Split(err.Error(), "\n")
for _, l := range lines[1:] { log.Printf("formula problem:%s", strings.TrimPrefix(l, " - ")) }
return err
} Try / catch
if verr := f.Validate(); verr != nil && strings.Contains(verr.Error(), "formula validation failed") { /* iterate bullets; fix each named path */ } Prevention
- Run Validate on every formula at load time and in CI.
- Require attach on all compose hooks via a pre-commit check.
- Keep formulas aligned with the current schema after upgrades.
When it happens
Trigger: Calling Validate (public method on the formula type) on a formula whose checks appended entries to errs — e.g. a compose hook with no `attach` field — and at least one problem exists.
Common situations: Loading a formula after a schema/version change, hand-editing a formula and omitting required fields, or importing a formula written for an older format.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- applying control flow: %w
- applying expansions: %w
- %q is not an aspect formula (type=%s)
- runtime mode requires all variables to have values Missing:
- formula %q: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/c2f9a6dd5900a37c.
Report an issue: GitHub.