{"record":{"id":"ad3a463243044fd6","repo":"twpayne/chezmoi","slug":"s-no-url","errorCode":null,"errorMessage":"%s: no URL","messagePattern":"(.+?): no URL","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/chezmoi/sourcestate.go","lineNumber":1804,"sourceCode":"\toptions *ReadOptions,\n) ([]byte, string, error) {\n\tvar firstURLStr string\n\tvar firstErr error\n\tfor _, urlStr := range append([]string{external.URL}, external.URLs...) {\n\t\tif urlStr == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tdata, err := s.getExternalDataRaw(ctx, externalRelPath, urlStr, external.RefreshPeriod, options)\n\t\tif err == nil {\n\t\t\treturn data, urlStr, nil\n\t\t}\n\t\tif firstURLStr == \"\" {\n\t\t\tfirstURLStr = urlStr\n\t\t\tfirstErr = err\n\t\t}\n\t}\n\tif firstURLStr == \"\" {\n\t\treturn nil, \"\", fmt.Errorf(\"%s: no URL\", externalRelPath)\n\t}\n\treturn nil, firstURLStr, firstErr\n}\n\n// getExternalDataRaw returns the raw data for external at externalRelPath,\n// possibly from the external cache.\nfunc (s *SourceState) getExternalDataRaw(\n\tctx context.Context,\n\texternalRelPath RelPath,\n\turlStr string,\n\trefreshPeriod Duration,\n\toptions *ReadOptions,\n) ([]byte, error) {\n\t// Handle file:// URLs by always reading from disk.\n\tswitch urlStruct, err := url.Parse(urlStr); {\n\tcase err != nil:\n\t\treturn nil, err\n\tcase urlStruct.Scheme == \"file\":","sourceCodeStart":1786,"sourceCodeEnd":1822,"githubUrl":"https://github.com/twpayne/chezmoi/blob/f901167e4685db90da56d6a2a19df642cb3e0247/internal/chezmoi/sourcestate.go#L1786-L1822","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Fix the url value in the .chezmoiexternal entry to be a valid absolute URL (percent-encode spaces, no stray whitespace)","Run chezmoi with --verbose or validate the URL with a quick url.Parse in Go to confirm it parses","Quote or template-escape the URL if it is generated from template values"],"exampleFix":"// before\n[.vim]\n    url = [\"https://example.com/my dotfiles.zip\"]\n// after\n[.vim]\n    url = [\"https://example.com/my%20dotfiles.zip\"]","handlingStrategy":"validation","validationCode":"// validate external URLs before applying\nfor _, u := range extCfg.URLs {\n    if _, err := url.Parse(strings.TrimSpace(u)); err != nil {\n        return fmt.Errorf(\"external %q has invalid url %q: %w\", extCfg.RelPath, u, err)\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep URLs ASCII and percent-encoded","Trim template-generated URL strings","Run chezmoi apply --dry-run in CI to catch config errors early"],"tags":["config","external","url-parsing"],"backgroundTag":"invalid-external-url","analyzedSha":"f901167e4685db90da56d6a2a19df642cb3e0247","analyzedAt":"2026-09-01T18:16:41.508Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T01:17:15.007Z"}