{"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/c9def3e214014c1188faabfc4a5bde5095139765/internal/getproviders/hash.go#L104-L140","documentation":"Raised in PackageMatchesHash (internal/getproviders/hash.go:122). A hash with the 'zh:' (HashSchemeZip / legacy zip-SHA) scheme was supplied, but the package location is an unpacked directory (PackageLocalDir), not a .zip archive (PackageLocalArchive). Legacy zip hashes are computed over the original archive bytes, so they can only be verified when the archive is still present. The function refuses to silently skip and returns this explicit error.","triggerScenarios":"Verifying an unpacked provider cache directory against a lockfile or lock entry that only carries a 'zh:' hash. The type assertion loc.(PackageLocalArchive) fails because loc is a PackageLocalDir.","commonSituations":"A lockfile written by an old Terraform containing only zh: hashes, used against an unpacked cache; a custom verification routine that pairs a zh: hash with a directory location; providers cached as unpacked directories by default.","solutions":["Supply or also accept an 'h1:' content hash (HashScheme1), which works for both unpacked dirs and archives.","Keep the original .zip archive and pass a PackageLocalArchive location when you must verify a zh: hash.","Use PackageMatchesAnyHash instead, which gracefully treats an incompatible scheme/location combination as a non-match rather than an error."],"exampleFix":"// before\nok, err := PackageMatchesHash(PackageLocalDir(\"/cache/hashicorp/aws\"), zhHash)\n// after\nok, err := PackageMatchesHash(PackageLocalArchive(\"/cache/hashicorp/aws.zip\"), zhHash)","handlingStrategy":"type-guard","validationCode":"// Match the hash scheme to the location type before verifying.\nfunc canVerifyZipHash(loc PackageLocation) bool {\n\t_, ok := loc.(PackageLocalArchive)\n\treturn ok\n}","typeGuard":"// Narrow a PackageLocation to a zip-backed archive before a zh: check.\nfunc asArchive(loc PackageLocation) (PackageLocalArchive, bool) {\n\ta, ok := loc.(PackageLocalArchive)\n\treturn a, ok\n}","tryCatchPattern":"if _, ok := loc.(PackageLocalArchive); !ok && want.Scheme() == HashSchemeZip {\n    // Skip zh: verification for unpacked dirs; use PackageMatchesAnyHash.\n    return false, nil\n}","preventionTips":["Always keep (or also accept) an h1: content hash so unpacked dirs can be verified.","Keep the original .zip when you need to verify zh: hashes.","Prefer PackageMatchesAnyHash when the location type or scheme may vary."],"tags":["hash","provider","verification","type-mismatch"],"analyzedSha":"c9def3e214014c1188faabfc4a5bde5095139765","analyzedAt":"2026-08-07T15:39:49.278Z","schemaVersion":2},"datasetVersion":"2026-08-07T21:17:07.882Z"}