{"record":{"id":"f77f838b9c7341b0","repo":"helm/helm","slug":"failed-to-install-crd-s-w","errorCode":null,"errorMessage":"failed to install CRD %s: %w","messagePattern":"failed to install CRD (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/action/install.go","lineNumber":200,"sourceCode":"\treturn i.registryClient\n}\n\nfunc (i *Install) installCRDs(crds []chart.CRD) error {\n\t// We do these one file at a time in the order they were read.\n\ttotalItems := []*resource.Info{}\n\tfor _, obj := range crds {\n\t\tif obj.File == nil {\n\t\t\treturn fmt.Errorf(\"failed to install CRD %s: file is empty\", obj.Name)\n\t\t}\n\n\t\tif obj.File.Data == nil {\n\t\t\treturn fmt.Errorf(\"failed to install CRD %s: file data is empty\", obj.Name)\n\t\t}\n\n\t\t// Read in the resources\n\t\tres, err := i.cfg.KubeClient.Build(bytes.NewBuffer(obj.File.Data), false)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to install CRD %s: %w\", obj.Name, err)\n\t\t}\n\n\t\tif len(res) == 0 {\n\t\t\treturn fmt.Errorf(\"failed to install CRD %s: resources are empty\", obj.Name)\n\t\t}\n\n\t\t// Send them to Kube\n\t\tif _, err := i.cfg.KubeClient.Create(\n\t\t\tres,\n\t\t\tkube.ClientCreateOptionServerSideApply(i.ServerSideApply, i.ForceConflicts)); err != nil {\n\t\t\t// If the error is CRD already exists, continue.\n\t\t\tif apierrors.IsAlreadyExists(err) {\n\t\t\t\tcrdName := obj.Name\n\t\t\t\ti.cfg.Logger().Debug(\"CRD is already present. Skipping\", \"crd\", crdName)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"failed to install CRD %s: %w\", obj.Name, err)\n\t\t}","sourceCodeStart":182,"sourceCodeEnd":218,"githubUrl":"https://github.com/helm/helm/blob/2a29f1770b62844b27197d2507377361d45ad7c0/pkg/action/install.go#L182-L218","documentation":"Thrown by Install.installCRDs (pkg/action/install.go:200) when cfg.KubeClient.Build fails to parse/decode the CRD file's bytes into resource.Info objects. Build is called with validation off, so this specifically means the YAML/JSON could not be decoded into Kubernetes objects (syntax errors, missing apiVersion/kind), as opposed to cluster-side validation. The CRD name and the decode error are wrapped.","triggerScenarios":"A crds/*.yaml file that is invalid YAML (tabs, bad indentation, unterminated block), a document missing apiVersion/kind, or a file containing something other than Kubernetes manifests. Happens at install time before the CRD is sent to the cluster.","commonSituations":"Hand-written CRD files with indentation mistakes; CRDs copied from docs with markdown artifacts; multi-document files where an accidental separator leaves an empty or malformed document; CRDs converted from v1beta1 by scripts that broke the YAML structure.","solutions":["Locate the failing file: the error names obj.Name, matching the crds/ path in the chart","Validate locally: extract the file and run `kubectl apply --dry-run=client -f crd.yaml` or a YAML linter","Fix syntax/structure (indentation, quoting, apiVersion: apiextensions.k8s.io/v1, kind: CustomResourceDefinition)","Add chart linting to CI (`helm lint`, kubeconform on crds/) to catch this before release"],"exampleFix":"# before: broken indentation inside crds/example.com_things.yaml\nspec:\n  group: example.com\n   names: # one space too many\n\n# after\nspec:\n  group: example.com\n  names:","handlingStrategy":"validation","validationCode":"// lint CRD files as part of chart CI\ndata, _ := os.ReadFile(\"crds/thing.yaml\")\nvar doc map[string]any\nif err := yaml.Unmarshal(data, &doc); err != nil {\n    return fmt.Errorf(\"crds/thing.yaml does not parse: %w\", err)\n}\nif doc[\"kind\"] != \"CustomResourceDefinition\" { return errors.New(\"not a CRD\") }","typeGuard":"func isParsableCRDFile(data []byte) bool {\n    var doc map[string]any\n    return yaml.Unmarshal(data, &doc) == nil && doc[\"apiVersion\"] != nil && doc[\"kind\"] != nil\n}","tryCatchPattern":"if strings.Contains(err.Error(), \"failed to install CRD\") {\n    // obj.Name in the message maps to the crds/ file; extract and run kubectl --dry-run=client on it\n}","preventionTips":["Run kubeconform/yamllint over crds/ in chart CI","Copy CRDs from generated sources (controller-gen) instead of hand-editing","Prefer apiextensions.k8s.io/v1 with structural schemas on all supported clusters"],"tags":["crd","yaml","chart","validation"],"backgroundTag":null,"analyzedSha":"2a29f1770b62844b27197d2507377361d45ad7c0","analyzedAt":"2026-08-15T22:02:47.490Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}