gastownhall/beads · error

unknown external type: %s

Error message

unknown external type: %s

What it means

evaluateExternal supports only two external check types: "file.exists" and "env". Any other Condition.ExternalType value falls through the switch and yields this error. ParseCondition is the only producer of external conditions and always sets one of the two valid values.

Source

Thrown at internal/formula/condition.go:412

			path = strings.ReplaceAll(path, "{{"+k+"}}", v)
		}
		_, err := os.Stat(path)
		exists := err == nil
		return &ConditionResult{
			Satisfied: exists,
			Reason:    fmt.Sprintf("file %q exists: %v", path, exists),
		}, nil

	case "env":
		actual := os.Getenv(c.ExternalArg)
		satisfied, reason := compare(actual, c.Operator, c.Value)
		return &ConditionResult{
			Satisfied: satisfied,
			Reason:    reason,
		}, nil
	}

	return nil, fmt.Errorf("unknown external type: %s", c.ExternalType)
}

// Helper functions

func unquote(s string) string {
	s = strings.TrimSpace(s)
	if len(s) >= 2 {
		if (s[0] == '\'' && s[len(s)-1] == '\'') || (s[0] == '"' && s[len(s)-1] == '"') {
			return s[1 : len(s)-1]
		}
	}
	return s
}

func getNestedValue(m map[string]interface{}, path string) interface{} {
	if m == nil {
		return nil
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Use only ExternalType "file.exists" (with ExternalArg=path) or "env" (with ExternalArg=var name, Operator, Value)
  2. Build external conditions with formula.ParseCondition, e.g. ParseCondition("file.exists('go.mod')") or ParseCondition("env.CI == 'true'")
  3. If a new check type is needed, extend ParseCondition/evaluateExternal in the library rather than hand-setting ExternalType

Example fix

// before
cond := &formula.Condition{Type: formula.ConditionTypeExternal, ExternalType: "http.ok", ExternalArg: "https://..."}
res, err := cond.Evaluate(ctx) // unknown external type: http.ok
// after
cond, _ := formula.ParseCondition("file.exists('go.mod')")
res, err := cond.Evaluate(ctx)
Defensive patterns

Strategy: validation

Validate before calling

func validExternalType(t string) bool { return t == "file.exists" || t == "env" }
if cond.Type == formula.ConditionTypeExternal && !validExternalType(cond.ExternalType) {
	return fmt.Errorf("external type %q must be file.exists or env", cond.ExternalType)
}

Type guard

func isKnownExternalType(t string) bool {
	return t == "file.exists" || t == "env"
}

Try / catch

res, err := cond.Evaluate(ctx)
if err != nil && strings.HasPrefix(err.Error(), "unknown external type") {
	return fmt.Errorf("external check %q not supported; use file.exists or env", cond.ExternalType)
}
return res, err

Prevention

When it happens

Trigger: Evaluate() on a Type=external Condition whose ExternalType is empty or an arbitrary string (e.g. "http.ok", "cmd.exit") — only possible when the struct is built or mutated outside ParseCondition.

Common situations: Trying to add custom external probes (HTTP checks, command exits) by writing ExternalType directly instead of extending the parser; struct-literal construction with a forgotten ExternalType; version drift where a newer/older writer emitted an unsupported type.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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