{"record":{"id":"e93700ef4c35574b","repo":"helm/helm","slug":"expected-config-media-type-s-for-legacy-compatibi","errorCode":null,"errorMessage":"expected config media type %s for legacy compatibility, got %s","messagePattern":"expected config media type (.+?) for legacy compatibility, got (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/registry/plugin.go","lineNumber":99,"sourceCode":"\tmanifestData, err := c.Generic().GetDescriptorData(genericResult.MemoryStore, genericResult.Manifest)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to retrieve manifest: %w\", err)\n\t}\n\n\t// Parse the manifest to check artifact type\n\tvar manifest ocispec.Manifest\n\tif err := json.Unmarshal(manifestData, &manifest); err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to parse manifest: %w\", err)\n\t}\n\n\t// Validate artifact type (for OCI v1.1+ manifests)\n\tif manifest.ArtifactType != \"\" && manifest.ArtifactType != PluginArtifactType {\n\t\treturn nil, fmt.Errorf(\"expected artifact type %s, got %s\", PluginArtifactType, manifest.ArtifactType)\n\t}\n\n\t// For backwards compatibility, also check config media type if no artifact type\n\tif manifest.ArtifactType == \"\" && manifest.Config.MediaType != PluginArtifactType {\n\t\treturn nil, fmt.Errorf(\"expected config media type %s for legacy compatibility, got %s\", PluginArtifactType, manifest.Config.MediaType)\n\t}\n\n\t// Find the plugin tarball and optional provenance using NAME-VERSION.tgz format\n\tvar pluginDescriptor *ocispec.Descriptor\n\tvar provenanceDescriptor *ocispec.Descriptor\n\tvar foundProvenanceName string\n\n\t// Look for layers with the expected titles/annotations\n\tfor _, layer := range manifest.Layers {\n\t\td := layer\n\t\t// Check for title annotation\n\t\tif title, exists := d.Annotations[ocispec.AnnotationTitle]; exists {\n\t\t\t// Check if this looks like a plugin tarball: {pluginName}-{version}.tgz\n\t\t\tif pluginDescriptor == nil && strings.HasPrefix(title, pluginName+\"-\") && strings.HasSuffix(title, \".tgz\") {\n\t\t\t\tpluginDescriptor = &d\n\t\t\t}\n\t\t\t// Check if this looks like a plugin provenance: {pluginName}-{version}.tgz.prov\n\t\t\tif provenanceDescriptor == nil && strings.HasPrefix(title, pluginName+\"-\") && strings.HasSuffix(title, \".tgz.prov\") {","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/helm/helm/blob/2a29f1770b62844b27197d2507377361d45ad7c0/pkg/registry/plugin.go#L81-L117","documentation":"Thrown by registry Client.PullPlugin (pkg/registry/plugin.go) after downloading an OCI manifest. For backwards compatibility, when a manifest has no OCI 1.1 artifactType Helm requires the manifest's config descriptor mediaType to equal PluginArtifactType (application/vnd.helm.plugin.v1+json). If the artifact has neither an artifactType nor that legacy config media type, it is not a Helm plugin and the pull is rejected.","triggerScenarios":"Calling Client.PullPlugin or `helm plugin pull`/`helm plugin install` from an oci:// ref whose manifest omits artifactType and whose config.mediaType is different, e.g. a Helm chart pushed with `helm push` (config media type application/vnd.cncf.helm.config.v1+json), a plain OCI image, or an artifact pushed with oras without the plugin config type.","commonSituations":"Pointing a plugin pull at a chart OCI ref instead of a plugin ref; plugins pushed with custom tooling that set a different config media type; automation scripts that mix chart and plugin references; artifacts pushed before the plugin artifact-type convention existed.","solutions":["Inspect the artifact: `crane manifest <ref>` and check artifactType and config.mediaType fields","Confirm the ref is a plugin artifact, not a chart; charts are pulled with `helm pull oci://...` not `helm plugin pull`","Re-push the plugin with `helm plugin push`, which sets the correct artifact/config media type","If pushing manually with oras, set --artifact-type application/vnd.helm.plugin.v1+json (or the config media type to the same value)"],"exampleFix":"# before (fails: chart artifact has no plugin config media type)\nhelm plugin pull oci://ghcr.io/user/mychart:1.0.0\n\n# after (plugin artifact pushed with: helm plugin push ./my-plugin oci://ghcr.io/user/my-plugin)\nhelm plugin pull oci://ghcr.io/user/my-plugin:1.0.0","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"res, err := client.PullPlugin(ref, pluginName)\nif err != nil {\n\tmsg := err.Error()\n\tif strings.Contains(msg, \"expected config media type\") || strings.Contains(msg, \"expected artifact type\") {\n\t\t// ref is not a Helm plugin artifact - route to chart pull or reject the input\n\t\treturn fmt.Errorf(\"%s is not a plugin artifact\", ref)\n\t}\n\treturn err\n}","preventionTips":["Push plugins exclusively with `helm plugin push` so artifact/config media types are set correctly","Never reuse chart OCI refs (helm push output) for plugin pulls","Verify artifacts with `crane manifest <ref>` before wiring automated pulls"],"tags":["oci","registry","plugin","media-type","pull"],"backgroundTag":null,"analyzedSha":"2a29f1770b62844b27197d2507377361d45ad7c0","analyzedAt":"2026-08-15T22:02:47.490Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}