{"record":{"id":"c77e733794f38842","repo":"hashicorp/terraform","slug":"ziphash-scheme-zh-prefix-is-not-supported-for","errorCode":null,"errorMessage":"ziphash scheme (\"zh:\" prefix) is not supported for unpacked provider packages","messagePattern":"ziphash scheme \\(\"zh:\" prefix\\) is not supported for unpacked provider packages","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/getproviders/hash.go","lineNumber":122,"sourceCode":"// if others are introduced in future PackageMatchesHash may accept multiple\n// formats, and may generate errors for any formats that become obsolete.\n//\n// PackageMatchesHash can be used only with the two local package location types\n// PackageLocalDir and PackageLocalArchive, because it needs to access the\n// contents of the indicated package in order to compute the hash. If given\n// a non-local location this function will always return an error.\nfunc PackageMatchesHash(loc PackageLocation, want providerreqs.Hash) (bool, error) {\n\tswitch want.Scheme() {\n\tcase HashScheme1:\n\t\tgot, err := PackageHashV1(loc)\n\t\tif err != nil {\n\t\t\treturn false, err\n\t\t}\n\t\treturn got == want, nil\n\tcase HashSchemeZip:\n\t\tarchiveLoc, ok := loc.(PackageLocalArchive)\n\t\tif !ok {\n\t\t\treturn false, fmt.Errorf(`ziphash scheme (\"zh:\" prefix) is not supported for unpacked provider packages`)\n\t\t}\n\t\tgot, err := PackageHashLegacyZipSHA(archiveLoc)\n\t\tif err != nil {\n\t\t\treturn false, err\n\t\t}\n\t\treturn got == want, nil\n\tdefault:\n\t\treturn false, fmt.Errorf(\"unsupported hash format (this may require a newer version of Terraform)\")\n\t}\n}\n\n// PackageMatchesAnyHash returns true if the package at the given location\n// matches at least one of the given hashes, or false otherwise.\n//\n// If it cannot read from the given location, PackageMatchesAnyHash returns an\n// error. Unlike the singular PackageMatchesHash, PackageMatchesAnyHash\n// considers unsupported hash formats as successfully non-matching, rather\n// than returning an error.","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/getproviders/hash.go#L104-L140","documentation":"`PackageMatchesHash` switches on the hash scheme. For `HashSchemeZip` (the legacy `zh:` prefix) it can only compute a comparison if the package location is a `PackageLocalArchive` (a `.zip` file). If the caller passes an unpacked directory (`PackageLocalDir`) but asks to match a `zh:` hash, the zip-hash cannot be computed for unpacked packages and this error is returned.","triggerScenarios":"`PackageMatchesHash(loc, want)` is called where `want.Scheme() == HashSchemeZip` and `loc` is not a `PackageLocalArchive` (e.g. it is a `PackageLocalDir`); the type assertion at hash.go:119 fails.","commonSituations":"A lock file pins `zh:` hashes but the provider cache holds an unpacked directory; mixing the legacy `zh:` scheme with the modern unpacked-package layout; lock file generated by an older Terraform against an archive-only install.","solutions":["Regenerate the lock file with current Terraform so it records `h1:` hashes compatible with unpacked packages.","Install the provider as an archive so the `zh:` comparison is valid, or switch the lock entries from `zh:` to `h1:`.","Use `PackageMatchesAnyHash` where unsupported schemes are treated as non-matching instead of erroring."],"exampleFix":"// before: lock pins zh: hash, cache is unpacked dir\nPackageMatchesHash(PackageLocalDir(p), zhHash) // -> error\n\n// after: regenerate lock with h1: hashes\nterraform providers lock -platform=linux_amd64\n// then PackageMatchesHash(PackageLocalDir(p), h1Hash) succeeds","handlingStrategy":"validation","validationCode":"// Only request zh: matching for archive locations\nswitch loc := loc.(type) {\ncase PackageLocalArchive:\n    return PackageMatchesHash(loc, want) // zh: ok\ndefault:\n    if want.Scheme() == HashSchemeZip {\n        // recompute as h1: or skip\n        return false, nil\n    }\n    return PackageMatchesHash(loc, want)\n}","typeGuard":"// canMatchZipHash reports whether loc can be matched against a zh: hash\nfunc canMatchZipHash(loc PackageLocation) bool {\n    _, ok := loc.(PackageLocalArchive)\n    return ok\n}","tryCatchPattern":"ok, err := PackageMatchesHash(loc, want)\nif err != nil && strings.Contains(err.Error(), \"ziphash scheme\") {\n    // fall back to any-hash matching which tolerates the mismatch\n    return PackageMatchesAnyHash(loc, []Hash{want})\n}","preventionTips":["Prefer `h1:` hashes in lock files; they work for both archives and unpacked dirs.","Regenerate lock files with the Terraform version actually in use.","Use `PackageMatchesAnyHash` for tolerant comparison.","Avoid mixing legacy `zh:` and modern `h1:` schemes in one lock file."],"tags":["hash","lock-file","ziphash","provider-cache"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}