larksuite/cli · error

Input.Relations[%d].Stage %q is invalid

Error message

Input.Relations[%d].Stage %q is invalid

What it means

compileRelations requires each relation's Stage to be typedStageSourcePreRun or typedStageAfterPrepare; anything else is rejected at compile time. Stage decides when the relation check runs (against raw CLI source flags or after the prepare phase), so an invalid stage would leave enforcement timing undefined.

Source

Thrown at shortcuts/common/typed_compile_contract.go:33

	seen := make(map[string]struct{})
	for i, definition := range definitions {
		minimum := 2
		exact := 0
		switch definition.Kind {
		case typedRelationExactlyOne, typedRelationAtLeastOne, typedRelationCoOccur, typedRelationConflicts:
		case typedRelationRequires:
			exact = 2
		default:
			return nil, fmt.Errorf("Input.Relations[%d].Kind %q is invalid", i, definition.Kind)
		}
		if exact > 0 && len(definition.Params) != exact || exact == 0 && len(definition.Params) < minimum {
			return nil, fmt.Errorf("Input.Relations[%d] kind %s has invalid param count %d", i, definition.Kind, len(definition.Params))
		}
		if definition.Presence != typedPresenceExplicit && definition.Presence != typedPresenceNonZero {
			return nil, fmt.Errorf("Input.Relations[%d].Presence %q is invalid", i, definition.Presence)
		}
		if definition.Stage != typedStageSourcePreRun && definition.Stage != typedStageAfterPrepare {
			return nil, fmt.Errorf("Input.Relations[%d].Stage %q is invalid", i, definition.Stage)
		}
		compiled := compiledRelation{kind: definition.Kind, presence: definition.Presence, stage: definition.Stage}
		local := make(map[string]struct{})
		for _, name := range definition.Params {
			field, ok := fieldByName[name]
			if !ok {
				return nil, fmt.Errorf("Input.Relations[%d] references unknown param --%s", i, name)
			}
			if _, duplicate := local[name]; duplicate {
				return nil, fmt.Errorf("Input.Relations[%d] repeats param --%s", i, name)
			}
			local[name] = struct{}{}
			compiled.fields = append(compiled.fields, field)
		}
		keyBytes, _ := json.Marshal(definition)
		key := string(keyBytes)
		if _, duplicate := seen[key]; duplicate {
			return nil, fmt.Errorf("Input.Relations[%d] duplicates an earlier relation", i)

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Set Stage to typedStageSourcePreRun or typedStageAfterPrepare explicitly
  2. Choose source_pre_run for checks on raw flags, after_prepare for checks that depend on prepared state
  3. Update any definitions copied from older code that used a different stage value

Example fix

// before
{Kind: typedRelationAtLeastOne, Params: []string{"a","b"}, Stage: ""}
// after
{Kind: typedRelationAtLeastOne, Params: []string{"a","b"}, Stage: typedStageSourcePreRun}
Defensive patterns

Strategy: validation

Validate before calling

func knownStage(s typedStage) bool {
	return s == typedStageSourcePreRun || s == typedStageAfterPrepare
}

Type guard

func isTypedStage(v any) bool {
	s, ok := v.(typedStage)
	return ok && knownStage(s)
}

Prevention

When it happens

Trigger: Setting Stage to an empty string (struct zero value) or an unknown string like "pre-run" in a typedRelation declaration.

Common situations: Omitting Stage when constructing relations literally; renaming or misremembering stage constants across versions.

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 larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/0849ac5a73d6e350. Report an issue: GitHub.