kubernetes/kops · error

addon manifest %q did not have a spec

Error message

addon manifest %q did not have a spec

What it means

BootstrapChannelBuilder.Normalize assembles the channelsapi.Addons object listing every addon manifest in the channel. Before appending, it asserts each manifest's addonSpec is non-nil; a nil spec means the manifest was never properly built (its spec/hash pipeline never ran), so the channel would be incomplete and kOps fails with the addon's name in the message.

Source

Thrown at upup/pkg/fi/cloudup/bootstrapchannelbuilder/bootstrapchannel.go:60

	_ fi.CloudupHasDependencies = (*BootstrapChannel)(nil)
)

func (a *BootstrapChannel) GetDependencies(tasks map[string]fi.CloudupTask) []fi.CloudupTask {
	dependencies := make([]fi.CloudupTask, 0, len(a.addonManifests))
	for _, manifest := range a.addonManifests {
		dependencies = append(dependencies, manifest)
	}
	return dependencies
}

func (a *BootstrapChannel) Normalize(c *fi.CloudupContext) error {
	addonsObject := &channelsapi.Addons{}
	addonsObject.Kind = "Addons"
	addonsObject.ObjectMeta.Name = "bootstrap"

	for _, manifest := range a.addonManifests {
		if manifest.addonSpec == nil {
			return fmt.Errorf("addon manifest %q did not have a spec", fi.ValueOf(manifest.Name))
		}
		if manifest.addonSpec.ManifestHash == "" {
			return fmt.Errorf("addon %q manifest hash was not populated", fi.ValueOf(manifest.addonSpec.Name))
		}
		addonsObject.Spec.Addons = append(addonsObject.Spec.Addons, manifest.addonSpec)
	}

	if err := addonsObject.Verify(); err != nil {
		return err
	}

	addonsYAML, err := utils.YamlMarshal(addonsObject)
	if err != nil {
		return fmt.Errorf("error serializing addons yaml: %v", err)
	}

	a.Contents = fi.NewBytesResource(addonsYAML)
	return nil

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Ensure every addon goes through the AddonManifest build helper so addonSpec is populated before BootstrapChannelBuilder.Normalize runs
  2. Check task ordering/registration: the per-manifest Normalize must execute before the channel's Normalize; verify task dependencies/wiring
  3. If you added a new addon recently, diff against an existing addon's builder code to spot the missing spec wiring
  4. Re-run kops update with --dry-run and check the full error context to identify the failing addon name

Example fix

// before
a.addonManifests = append(a.addonManifests, &AddonManifest{Name: fi.PtrTo("my-addon"), source: res})
// after
m := buildAddonManifest(b, "my-addon", spec, res)
a.addonManifests = append(a.addonManifests, m) // spec wired via builder
Defensive patterns

Strategy: validation

Validate before calling

for _, m := range a.addonManifests {
    if m.addonSpec == nil {
        return fmt.Errorf("addon %s registered without spec; ensure AddonManifest.Normalize ran", fi.ValueOf(m.Name))
    }
}

Type guard

func manifestHasSpec(m *AddonManifest) bool { return m != nil && m.addonSpec != nil }

Prevention

When it happens

Trigger: Normalize runs over a.addonManifests and encounters an entry whose addonSpec field is nil — typically an AddonManifest registered in the builder whose Normalize (addonmanifest.go) never ran or never populated the spec, or one built manually in tests without a spec.

Common situations: A new addon added to the channel list without going through the buildAddonManifest pipeline; ordering issue where BootstrapChannelBuilder.Normalize runs before the manifest tasks are normalized; hand-written AddonManifest in custom builds.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05). Data as JSON: /api/errors/529b04f155f85c77. Report an issue: GitHub.