{"record":{"id":"529b04f155f85c77","repo":"kubernetes/kops","slug":"addon-manifest-q-did-not-have-a-spec","errorCode":null,"errorMessage":"addon manifest %q did not have a spec","messagePattern":"addon manifest %q did not have a spec","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/cloudup/bootstrapchannelbuilder/bootstrapchannel.go","lineNumber":60,"sourceCode":"\t_ fi.CloudupHasDependencies = (*BootstrapChannel)(nil)\n)\n\nfunc (a *BootstrapChannel) GetDependencies(tasks map[string]fi.CloudupTask) []fi.CloudupTask {\n\tdependencies := make([]fi.CloudupTask, 0, len(a.addonManifests))\n\tfor _, manifest := range a.addonManifests {\n\t\tdependencies = append(dependencies, manifest)\n\t}\n\treturn dependencies\n}\n\nfunc (a *BootstrapChannel) Normalize(c *fi.CloudupContext) error {\n\taddonsObject := &channelsapi.Addons{}\n\taddonsObject.Kind = \"Addons\"\n\taddonsObject.ObjectMeta.Name = \"bootstrap\"\n\n\tfor _, manifest := range a.addonManifests {\n\t\tif manifest.addonSpec == nil {\n\t\t\treturn fmt.Errorf(\"addon manifest %q did not have a spec\", fi.ValueOf(manifest.Name))\n\t\t}\n\t\tif manifest.addonSpec.ManifestHash == \"\" {\n\t\t\treturn fmt.Errorf(\"addon %q manifest hash was not populated\", fi.ValueOf(manifest.addonSpec.Name))\n\t\t}\n\t\taddonsObject.Spec.Addons = append(addonsObject.Spec.Addons, manifest.addonSpec)\n\t}\n\n\tif err := addonsObject.Verify(); err != nil {\n\t\treturn err\n\t}\n\n\taddonsYAML, err := utils.YamlMarshal(addonsObject)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error serializing addons yaml: %v\", err)\n\t}\n\n\ta.Contents = fi.NewBytesResource(addonsYAML)\n\treturn nil","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/cloudup/bootstrapchannelbuilder/bootstrapchannel.go#L42-L78","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure every addon goes through the AddonManifest build helper so addonSpec is populated before BootstrapChannelBuilder.Normalize runs","Check task ordering/registration: the per-manifest Normalize must execute before the channel's Normalize; verify task dependencies/wiring","If you added a new addon recently, diff against an existing addon's builder code to spot the missing spec wiring","Re-run kops update with --dry-run and check the full error context to identify the failing addon name"],"exampleFix":"// before\na.addonManifests = append(a.addonManifests, &AddonManifest{Name: fi.PtrTo(\"my-addon\"), source: res})\n// after\nm := buildAddonManifest(b, \"my-addon\", spec, res)\na.addonManifests = append(a.addonManifests, m) // spec wired via builder","handlingStrategy":"validation","validationCode":"for _, m := range a.addonManifests {\n    if m.addonSpec == nil {\n        return fmt.Errorf(\"addon %s registered without spec; ensure AddonManifest.Normalize ran\", fi.ValueOf(m.Name))\n    }\n}","typeGuard":"func manifestHasSpec(m *AddonManifest) bool { return m != nil && m.addonSpec != nil }","tryCatchPattern":null,"preventionTips":["Register addons only via buildAddonManifest so specs are always populated","Verify task normalization ordering: per-manifest Normalize before channel Normalize","Add a builder-level test that iterates all addons asserting non-nil specs"],"tags":["addons","bootstrap-channel","validation"],"backgroundTag":"missing-required-argument","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T07:17:12.445Z"}