{"record":{"id":"6269e7fc2999b8a8","repo":"kubernetes/kops","slug":"addon-spec-is-not-configured-for-q","errorCode":null,"errorMessage":"addon spec is not configured for %q","messagePattern":"addon spec is not configured for %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/cloudup/bootstrapchannelbuilder/addonmanifest.go","lineNumber":78,"sourceCode":")\n\n// GetDependencies makes the addon wait for every non-addon task, so templates that reach into\n// the task graph see fully realized state. Intentionally pessimistic: addon rendering is fast\n// enough that over-depending is not worth the footgun of under-declaring.\nfunc (a *AddonManifest) GetDependencies(tasks map[string]fi.CloudupTask) []fi.CloudupTask {\n\tdependencies := make([]fi.CloudupTask, 0, len(tasks))\n\tfor _, task := range tasks {\n\t\tif isAddonTask(task) {\n\t\t\tcontinue\n\t\t}\n\t\tdependencies = append(dependencies, task)\n\t}\n\treturn dependencies\n}\n\nfunc (a *AddonManifest) Normalize(c *fi.CloudupContext) error {\n\tif a.addonSpec == nil {\n\t\treturn fmt.Errorf(\"addon spec is not configured for %q\", fi.ValueOf(a.Name))\n\t}\n\tif a.source == nil {\n\t\treturn fmt.Errorf(\"addon source is not configured for %q\", fi.ValueOf(a.Name))\n\t}\n\n\tmanifestBytes, err := fi.ResourceAsBytes(a.source)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error reading addon %q manifest: %v\", fi.ValueOf(a.Name), err)\n\t}\n\n\tif !a.skipRender && a.addonRenderer != nil {\n\t\tmanifestBytes, err = a.addonRenderer.RenderTemplate(fi.ValueOf(a.Location), manifestBytes, tasksVisibleToAddons(c.AllTasks()))\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error rendering addon %q template: %w\", fi.ValueOf(a.Name), err)\n\t\t}\n\t}\n\n\tif !a.skipRemap {","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/cloudup/bootstrapchannelbuilder/addonmanifest.go#L60-L96","documentation":"AddonManifest.Normalize validates that the internal addonSpec field has been populated before doing any manifest processing. addonSpec is set when the addon manifest task is built from the bootstrap channel builder; if it is nil, subsequent steps (remapping, hashing, pruning) could not work, so kOps aborts with this message naming the addon.","triggerScenarios":"Normalize runs (during CloudupContext task normalization, exercised by TestAddonManifestNormalizeSkipsRenderForRawSources / TestAddonManifestNormalizeRendersTemplateSources) on an AddonManifest that was created via a constructor or struct literal that never called the setter for addonSpec — e.g. a builder path that set a.source but skipped building the addons.AddonSpec.","commonSituations":"A newly added addon in bootstrapchannelbuilder whose AddonManifest is constructed with an incomplete builder chain; a refactor renaming/moving the spec-building method so it is no longer called; test code building AddonManifest by hand without the spec.","solutions":["Check the code path that constructs this AddonManifest in bootstrapchannelbuilder and ensure addonSpec (built from the channel's addons.AddonSpec) is assigned before Normalize runs","If you added a new addon, follow the existing builder pattern: buildAddonManifest(..., spec, ...) so the spec is wired in","In tests, populate the spec field (or use the package builder helper) rather than a bare struct literal","Verify with go test ./upup/pkg/fi/cloudup/bootstrapchannelbuilder/... to see which addon name is reported and trace its builder call"],"exampleFix":"// before\nm := &AddonManifest{Name: fi.PtrTo(\"coredns\"), source: res}\n// after\nm := &AddonManifest{Name: fi.PtrTo(\"coredns\"), source: res, addonSpec: spec}\nwhere spec := &addons.AddonSpec{Name: fi.PtrTo(\"coredns.addons.k8s.io\"), ...}","handlingStrategy":"validation","validationCode":"if m.addonSpec == nil {\n    return fmt.Errorf(\"addon %s: addonSpec must be set before Normalize\", fi.ValueOf(m.Name))\n}","typeGuard":"func (a *AddonManifest) HasSpec() bool { return a.addonSpec != nil }","tryCatchPattern":null,"preventionTips":["Always build AddonManifest through buildAddonManifest, never via bare struct literals","Add a constructor that requires addonSpec so it cannot be omitted","Run the bootstrapchannelbuilder unit tests after adding new addons"],"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-12T12:17:11.808Z"}