gastownhall/beads · error

unknown field: %s

Error message

unknown field: %s

What it means

evaluateField only understands two field shapes on a step: the literal field "status" and any path starting with "output.". Any other field name in a field-type condition is rejected at evaluation time with this error.

Source

Thrown at internal/formula/condition.go:268

	}

	step, ok := ctx.Steps[stepID]
	if !ok {
		return &ConditionResult{
			Satisfied: false,
			Reason:    fmt.Sprintf("step %q not found", stepID),
		}, nil
	}

	// Get the field value
	var actual interface{}
	if c.Field == "status" {
		actual = step.Status
	} else if strings.HasPrefix(c.Field, "output.") {
		path := strings.TrimPrefix(c.Field, "output.")
		actual = getNestedValue(step.Output, path)
	} else {
		return nil, fmt.Errorf("unknown field: %s", c.Field)
	}

	// Compare
	satisfied, reason := compare(actual, c.Operator, c.Value)
	return &ConditionResult{
		Satisfied: satisfied,
		Reason:    reason,
	}, nil
}

func (c *Condition) evaluateAggregate(ctx *ConditionContext) (*ConditionResult, error) {
	// Get the set of steps to aggregate over
	var steps []*StepState

	switch c.AggregateOver {
	case "children":
		stepID := c.StepRef
		if stepID == "step" {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Prefix step-output references with `output.`: use `step.output.approved == 'true'`, not `step.approved`
  2. Only use the supported field `status` for step status checks: `step.status == 'complete'`
  3. Pre-validate the field path before evaluating: accept only "status" or strings with the "output." prefix

Example fix

// before
cond, _ := formula.ParseCondition("step.approved == 'true'")
res, err := cond.Evaluate(ctx) // unknown field: approved
// after
cond, _ := formula.ParseCondition("step.output.approved == 'true'")
res, err := cond.Evaluate(ctx)
Defensive patterns

Strategy: validation

Validate before calling

func validField(f string) bool { return f == "status" || strings.HasPrefix(f, "output.") }
if !validField(cond.Field) {
	return fmt.Errorf("field %q unsupported; use status or output.<path>", cond.Field)
}

Type guard

func isSupportedField(field string) bool {
	return field == "status" || strings.HasPrefix(field, "output.")
}

Try / catch

res, err := cond.Evaluate(ctx)
if err != nil {
	var msg string
	if _, scan := fmt.Sscanf(err.Error(), "unknown field: %s", &msg); scan == nil {
		return fmt.Errorf("bad condition field %q: only 'status' and 'output.*' are allowed", msg)
	}
	return err
}

Prevention

When it happens

Trigger: Evaluate() on a Condition of Type=field whose Field is neither "status" nor "output.*" — e.g. parsing `step.name == 'foo'` (fieldPattern accepts any dotted path) then evaluating it, or `review.title == 'x'`.

Common situations: Typos like `step.statuses`; using fields that exist on the underlying step object but are not exposed to the condition language (id, title, owner); assuming output fields are referenced without the `output.` prefix (`step.approved == true` instead of `step.output.approved == true`).

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/46fd5c2e30b146df. Report an issue: GitHub.