twpayne/chezmoi · error

%s: no URL

Error message

%s: no URL

What it means

Returned by SourceState.readExternalData when an external entry declares no usable URL: every url in the external's url list failed to parse, so firstURLStr was never set. chezmoi requires at least one valid URL to fetch external assets from.

Source

Thrown at internal/chezmoi/sourcestate.go:1804

	options *ReadOptions,
) ([]byte, string, error) {
	var firstURLStr string
	var firstErr error
	for _, urlStr := range append([]string{external.URL}, external.URLs...) {
		if urlStr == "" {
			continue
		}
		data, err := s.getExternalDataRaw(ctx, externalRelPath, urlStr, external.RefreshPeriod, options)
		if err == nil {
			return data, urlStr, nil
		}
		if firstURLStr == "" {
			firstURLStr = urlStr
			firstErr = err
		}
	}
	if firstURLStr == "" {
		return nil, "", fmt.Errorf("%s: no URL", externalRelPath)
	}
	return nil, firstURLStr, firstErr
}

// getExternalDataRaw returns the raw data for external at externalRelPath,
// possibly from the external cache.
func (s *SourceState) getExternalDataRaw(
	ctx context.Context,
	externalRelPath RelPath,
	urlStr string,
	refreshPeriod Duration,
	options *ReadOptions,
) ([]byte, error) {
	// Handle file:// URLs by always reading from disk.
	switch urlStruct, err := url.Parse(urlStr); {
	case err != nil:
		return nil, err
	case urlStruct.Scheme == "file":

View on GitHub (pinned to f901167e46)

Solutions

  1. Fix the url value in the .chezmoiexternal entry to be a valid absolute URL (percent-encode spaces, no stray whitespace)
  2. Run chezmoi with --verbose or validate the URL with a quick url.Parse in Go to confirm it parses
  3. Quote or template-escape the URL if it is generated from template values

Example fix

// before
[.vim]
    url = ["https://example.com/my dotfiles.zip"]
// after
[.vim]
    url = ["https://example.com/my%20dotfiles.zip"]
Defensive patterns

Strategy: validation

Validate before calling

// validate external URLs before applying
for _, u := range extCfg.URLs {
    if _, err := url.Parse(strings.TrimSpace(u)); err != nil {
        return fmt.Errorf("external %q has invalid url %q: %w", extCfg.RelPath, u, err)
    }
}

Prevention

When it happens

Trigger: Running chezmoi apply/init with an .chezmoiexternal entry whose url list contains only URLs that fail url.Parse (e.g. containing spaces or invalid characters), so no firstURLStr is ever recorded before the check.

Common situations: Hand-edited .chezmoiexternal.toml/yaml with a malformed URL (unescaped spaces, stray characters); templating that produced an empty or broken URL; copy-pasted URLs with whitespace.

Related errors


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