{"record":{"id":"3a19a305786ba04a","repo":"kubernetes/kops","slug":"addon-q-manifest-hash-was-not-populated","errorCode":null,"errorMessage":"addon %q manifest hash was not populated","messagePattern":"addon %q manifest hash was not populated","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/cloudup/bootstrapchannelbuilder/bootstrapchannel.go","lineNumber":63,"sourceCode":"func (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\n}\n\nfunc (a *BootstrapChannel) Find(c *fi.CloudupContext) (*BootstrapChannel, error) {","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/cloudup/bootstrapchannelbuilder/bootstrapchannel.go#L45-L81","documentation":"In the same loop, after confirming addonSpec exists, BootstrapChannelBuilder.Normalize requires addonSpec.ManifestHash to be non-empty. The hash is what lets the channels controller detect manifest changes; an empty hash means AddonManifest.Normalize never completed (hashing happens at its end) or the hash step was skipped, so the channel would be built with an unverifiable addon.","triggerScenarios":"Normalize iterates a.addonManifests and finds an entry with a non-nil addonSpec but empty ManifestHash — i.e. the manifest's own Normalize did not run to completion (aborted earlier, was not invoked, or skip paths bypassed hashing) before the channel builder aggregated the specs.","commonSituations":"Custom kOps builds that register addon specs directly into the channel without running AddonManifest.Normalize; a skipped/failed hashing step upstream that did not abort the whole run; refactors that decouple spec creation from manifest normalization.","solutions":["Ensure AddonManifest.Normalize runs (and reaches the end) for every addon before BootstrapChannelBuilder.Normalize; the hash is set there","Check for any early return/error swallowed upstream — the addon's own Normalize may have failed silently and left the hash empty","In custom code, never hand-set addonSpec into the channel; always route through the AddonManifest pipeline","Re-run with kops update --dry-run -v to trace whether the per-manifest Normalize executes for the failing addon"],"exampleFix":"// before\naddonsObject.Spec.Addons = append(addonsObject.Spec.Addons, spec) // hash never computed\n// after\nm := buildAddonManifest(b, name, spec, res)\nif err := m.Normalize(ctx); err != nil { return err }\naddonsObject.Spec.Addons = append(addonsObject.Spec.Addons, m.addonSpec)","handlingStrategy":"validation","validationCode":"for _, m := range a.addonManifests {\n    if m.addonSpec != nil && m.addonSpec.ManifestHash == \"\" {\n        return fmt.Errorf(\"addon %s: run AddonManifest.Normalize to compute ManifestHash\", fi.ValueOf(m.addonSpec.Name))\n    }\n}","typeGuard":"func manifestHashed(m *AddonManifest) bool { return m.addonSpec != nil && m.addonSpec.ManifestHash != \"\" }","tryCatchPattern":null,"preventionTips":["Never insert addonSpec objects into the channel directly; always go through AddonManifest.Normalize which sets ManifestHash","Check for swallowed errors from per-manifest Normalize during updates","Trace with kops update -v that each addon's Normalize completes before channel aggregation"],"tags":["addons","bootstrap-channel","hashing","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"}