hashicorp/packer · error
%s: %w
Error message
%s: %w
What it means
loadIndex fetches index.json for a plugin type and passes the bytes to parseIndex; on parse failure it wraps the parse error with the URL prefix ("%s: %w") so callers (versionURL, Get) can see which remote index was bad. It is a contextual wrapper — the root cause is error 334, 335, or 336.
Source
Thrown at packer/plugin-getter/remote/getter.go:168
dir = path.Join(append(append([]string{}, parts[1:len(parts)-1]...), pluginType)...)
return dir, pluginType
}
// loadIndex fetches and validates the plugin's index.json, reusing the
// parsed result across calls for the same plugin.
func (g *Getter) loadIndex(opts plugingetter.GetOptions) (*pluginIndex, string, error) {
dir, pluginType := pluginPath(opts)
if g.index != nil && g.index.pluginType == pluginType {
return g.index, dir, nil
}
url := g.BaseURL + "/" + dir + "/index.json"
data, err := g.fetch(url)
if err != nil {
return nil, "", err
}
idx, err := parseIndex(data, pluginType)
if err != nil {
return nil, "", fmt.Errorf("%s: %w", url, err)
}
g.index = idx
return idx, dir, nil
}
// versionURL resolves the version directory URL for the version being
// fetched, failing when the index does not list it.
func (g *Getter) versionURL(opts plugingetter.GetOptions) (*pluginIndex, string, error) {
idx, dir, err := g.loadIndex(opts)
if err != nil {
return nil, "", err
}
version := opts.VersionString()
if _, ok := idx.versions[version]; !ok {
return nil, "", fmt.Errorf(
"version %s of %s is not listed in %s/%s/index.json",
version, idx.pluginType, g.BaseURL, dir)
}View on GitHub (pinned to eb36e3c3e4)
Solutions
- Open the URL printed in the error and inspect what the index endpoint actually returns.
- Delete the cached index.json and re-run the install to force a clean re-fetch.
- Fix the root cause per the wrapped inner error (malformed JSON / no versions / no usable versions).
Defensive patterns
Strategy: try-catch
Validate before calling
data, err := fetch(indexURL)
if err == nil {
var probe struct{ Versions map[string]json.RawMessage `json:"versions"` }
if json.Unmarshal(data, &probe) != nil || len(probe.Versions) == 0 {
os.Remove(cachePath) // preempt the wrapped loadIndex failure
}
} Try / catch
if _, err := g.Get("releases", opts); err != nil {
if strings.Contains(err.Error(), "malformed index.json") ||
strings.Contains(err.Error(), "no usable versions") {
// errors.Is/errors.As on the wrapped inner error; purge cache and refetch
}
} Prevention
- Treat the URL prefix in the message as the pointer to the bad index endpoint.
- Unwrap with errors.Is/errors.As to distinguish JSON vs version-set root causes.
- Purge cached index.json after network interruptions or proxy changes.
When it happens
Trigger: Get or versionURL triggers loadIndex; the fetched index.json at `url` is invalid JSON, empty of versions, or contains no usable semver versions.
Common situations: Corrupted cached index.json on disk; proxy returning HTML for the index URL; custom registry serving an empty or non-semver index.
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
Related errors
- %q not implemented
- malformed filename expected %s{version}_{os}_{arch}
- wrong version: %s does not match expected %s
- wrong system, expected %s_%s got %s_%s
- malformed index.json: %w
AI-assisted analysis of hashicorp/packer@eb36e3c3e4 (2026-09-05).
Data as JSON: /api/errors/df1b6a3fcf08142e.
Report an issue: GitHub.