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

  1. Diff providers_mirror.go around the indexArchives[version][platform] assignment — confirm values stay as the documented {url string, hashes []string} shape.
  2. Add a focused unit test that builds indexArchives for one version/platform and asserts json.MarshalIndent succeeds.
  3. 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

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


AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11). Data as JSON: /api/errors/ddd07b1defb4f21b. Report an issue: GitHub.