hashicorp/terraform · critical
failed to encode version index: %s
Error message
failed to encode version index: %s
What it means
This panic fires when json.MarshalIndent fails to encode a per-version '<version>.json' index in providers_mirror.go:344. Like error 1000, the versionIndex map is internally constructed, so the failure indicates a programming regression that inserted an unmarshallable value type into archiveIndex.
Source
Thrown at internal/command/providers_mirror.go:344
// we're supporting network mirrors, to avoid having them briefly
// become corrupted during updates.
err = ioutil.WriteFile(filepath.Join(indexDir, "index.json"), mainIndexJSON, 0644)
if err != nil {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Failed to update indexes",
fmt.Sprintf("Failed to write an updated JSON index for %s: %s.", provider, err),
))
}
for version, archiveIndex := range indexArchives {
versionIndex := map[string]interface{}{
"archives": archiveIndex,
}
versionIndexJSON, err := json.MarshalIndent(versionIndex, "", " ")
if err != nil {
// Should never happen because the input here is entirely under
// our control.
panic(fmt.Sprintf("failed to encode version index: %s", err))
}
err = ioutil.WriteFile(filepath.Join(indexDir, version.String()+".json"), versionIndexJSON, 0644)
if err != nil {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Failed to update indexes",
fmt.Sprintf("Failed to write an updated JSON index for %s v%s: %s.", provider, version, err),
))
}
}
}
c.showDiagnostics(diags)
if diags.HasErrors() {
return 1
}
return 0
}View on GitHub (pinned to d32a084675)
Solutions
- Diff providers_mirror.go around the indexArchives[version][platform] assignment — confirm values stay as the documented {url string, hashes []string} shape.
- Add a focused unit test that builds indexArchives for one version/platform and asserts json.MarshalIndent succeeds.
- Convert any new field to its string/[]string JSON form before inserting into archiveIndex.
Example fix
// before
indexArchives[version][platform.String()] = map[string]interface{}{
"url": archiveURL, // url.URL struct — not JSON-friendly
"hashes": []string{hash.String()},
}
// after
indexArchives[version][platform.String()] = map[string]interface{}{
"url": archiveURL.String(), // plain string
"hashes": []string{hash.String()},
} Defensive patterns
Strategy: validation
Validate before calling
// Probe-serialize each archiveIndex before the real MarshalIndent.
for ver, idx := range indexArchives {
if _, err := json.Marshal(idx); err != nil {
return fmt.Errorf("archive index for %s not serializable: %w", ver, err)
}
} Type guard
// n/a — map[string]map[string]interface{}; guard via marshalling probe Prevention
- Constrain archiveIndex values to {url string, hashes []string}.
- Unit-test the marshal path for one version/platform in providers_mirror_test.go.
- Codify the index shape in a typed struct (e.g. type archiveEntry struct{ URL string; Hashes []string }) so the compiler enforces JSON-friendliness.
When it happens
Trigger: Invoked during `terraform providers mirror` for each provider version, when serializing the 'archives' sub-map. Fires only if an archiveIndex entry contains a type encoding/json cannot serialize (func, chan, recursive struct, unsupported map key).
Common situations: A refactor changes the archiveIndex value shape (e.g. adds a url.URL struct or a hash.Hash instead of the plain string/array) and the mirror command panics on the first version it processes.
Related errors
- failed to encode main index: %s
- backendDiags.Err()
- expected one provider requirement for the destination state
- expected exactly one provider requirement for the destinatio
- expected exactly one provider requirement for the destinatio
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/ddd07b1defb4f21b.
Report an issue: GitHub.