GoogleContainerTools/skaffold · error

invalid component specified in custom template: %v

Error message

invalid component specified in custom template: %v

What it means

A template field resolved to a customTemplateTagger, i.e. a custom template component referenced itself (directly or transitively), which would cause infinite recursion. `EvaluateComponents` rejects this with this error naming the offending component.

Source

Thrown at pkg/skaffold/tag/custom_template.go:76

	// missingkey=error throws error when map is indexed with an undefined key
	tag, err := ExecuteCustomTemplate(t.Template.Option("missingkey=error"), customMap)
	if err != nil {
		return "", err
	}

	return tag, nil
}

// EvaluateComponents creates a custom mapping of component names to their tagger string representation.
func (t *customTemplateTagger) EvaluateComponents(ctx context.Context, image latest.Artifact) (map[string]string, error) {
	taggers := map[string]Tagger{}

	templateFields := GetTemplateFields(t.Template)
	for _, field := range templateFields {
		if v, ok := t.Components[field]; ok {
			if _, ok := v.(*customTemplateTagger); ok {
				return nil, fmt.Errorf("invalid component specified in custom template: %v", v)
			}
			taggers[field] = v
			continue
		}
		switch field {
		case "GIT":
			gitTagger, _ := NewGitCommit("", "", false)
			taggers[field] = gitTagger
		case "DATE":
			taggers[field] = NewDateTimeTagger("", "")
		case "SHA":
			taggers[field] = &ChecksumTagger{}
		case "INPUT_DIGEST":
			inputDigestTagger, _ := NewInputDigestTagger(t.RunCtx, graph.ToArtifactGraph(t.RunCtx.Artifacts()))
			taggers[field] = inputDigestTagger
		default:
			return nil, fmt.Errorf("no tagger available for component %+v", field)
		}

View on GitHub (pinned to a1189de023)

Solutions

  1. Remove the nested custom-template component from the components map
  2. Use a primitive tagger component (DATE, SHA, GIT, LABEL, DIGEST, INPUT_DIGEST) for that field instead
  3. Refactor so the inner template's fields are expanded into the outer template directly

Example fix

// before: component map with recursion
components: {SUB: customTemplateTagger}
customTemplate: "{{.SUB}}-{{.SHA}}"
// after: reference primitives only
customTemplate: "{{.GIT}}-{{.SHA}}"
Defensive patterns

Strategy: validation

Validate before calling

// ensure no component maps to a custom template tagger (no recursion)
for name, comp := range components {
    if _, bad := comp.(*tag.CustomTemplateTagger); bad {
        return fmt.Errorf("component %q must not be a custom template tagger", name)
    }
}

Type guard

func isPrimitiveTagger(t tag.Tagger) bool {
    switch t.(type) {
    case *tag.CustomTemplateTagger:
        return false
    default:
        return true
    }
}

Prevention

When it happens

Trigger: `GenerateTag` calls `EvaluateComponents`; `GetTemplateFields` finds a field present in `t.Components`, but the value's concrete type is `*customTemplateTagger` — meaning a custom template component is nested inside another custom template's fields.

Common situations: Defining a custom tagger whose template includes a field mapped to another (or the same) custom template tagger in the components map; misconfigured `components` wiring that creates a cycle.

Related errors


AI-assisted analysis of GoogleContainerTools/skaffold@a1189de023 (2026-09-05). Data as JSON: /api/errors/c8b1f32546d67a18. Report an issue: GitHub.