twpayne/chezmoi · error

%s: %s: %s

Error message

%s: %s: %s

What it means

Returned by SourceState.getExternalDataRaw when the HTTP response status is outside 200-299 (and not a multiple-choice redirect). It wraps the URL and the server's status text so the developer sees which remote request failed and why.

Source

Thrown at internal/chezmoi/sourcestate.go:1884

	if err != nil {
		return nil, err
	}
	resp, err := chezmoilog.LogHTTPRequest(ctx, s.logger, s.httpClient, req)
	if err != nil {
		return nil, err
	}
	var data []byte
	if options == nil || options.ReadHTTPResponse == nil {
		data, err = io.ReadAll(resp.Body)
	} else {
		data, err = options.ReadHTTPResponse(urlStr, resp)
	}
	_ = resp.Body.Close()
	if err != nil {
		return nil, err
	}
	if resp.StatusCode < http.StatusOK || http.StatusMultipleChoices <= resp.StatusCode {
		return nil, fmt.Errorf("%s: %s: %s", externalRelPath, urlStr, resp.Status)
	}

	if err := MkdirAll(s.baseSystem, cachedDataAbsPath.Dir(), 0o700); err != nil {
		return nil, err
	}
	if err := s.baseSystem.WriteFile(cachedDataAbsPath, data, 0o600); err != nil {
		return nil, err
	}
	if err := s.baseSystem.Chtimes(cachedDataAbsPath, now, now); err != nil {
		return nil, err
	}

	return data, nil
}

// newCreateTargetStateEntryFunc returns a targetStateEntryFunc that returns a
// file with the value of sourceContentsFunc if the file does not already exist,
// or returns the actual file's contents unchanged if the file already exists.

View on GitHub (pinned to f901167e46)

Solutions

  1. Open the url from the error in a browser/curl and fix it in .chezmoiexternal (e.g. update to the new release tag)
  2. If authentication is required, configure credentials or use a token-authenticated URL
  3. Add a fallback second url entry so chezmoi can retry with a mirror

Example fix

// before
url = "https://github.com/user/repo/archive/v1.2.3.tar.gz"
// after
url = "https://github.com/user/repo/archive/v1.2.4.tar.gz"
Defensive patterns

Strategy: retry

Validate before calling

// check URL reachability before pinning it
resp, err := http.Head(url); if err != nil || resp.StatusCode != 200 { /* pick another url */ }

Try / catch

// wrap apply and retry once on non-2xx
if err := chezmoiApply(); err != nil {
    if strings.Contains(err.Error(), ": 4") || strings.Contains(err.Error(), ": 5") {
        refreshExternalURL(); retry()
    }
}

Prevention

When it happens

Trigger: chezmoi downloads an external asset (archive/file) and the server replies 404, 403, 500, etc.; resp.StatusCode < 200 || >= 300 triggers this before caching the data.

Common situations: External URL points at a moved/renamed GitHub release asset (404); private repo asset fetched without auth (403); rate-limited or misconfigured mirror (5xx); tag changed upstream.

Related errors


AI-assisted analysis of twpayne/chezmoi@f901167e46 (2026-09-01). Data as JSON: /api/errors/e900e4ed7af4d92b. Report an issue: GitHub.