{"record":{"id":"43ba5391a3f03eca","repo":"twpayne/chezmoi","slug":"s-s-path-is-not-relative","errorCode":null,"errorMessage":"%s: %s: path is not relative","messagePattern":"(.+?): (.+?): path is not relative","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/chezmoi/sourcestate.go","lineNumber":1459,"sourceCode":"\t\treturn fmt.Errorf(\"%s: %w\", sourceAbsPath, err)\n\t}\n\texternals := make(map[string]External)\n\tif err := format.Unmarshal(data, &externals); err != nil {\n\t\treturn fmt.Errorf(\"%s: %w\", sourceAbsPath, err)\n\t}\n\ts.mutex.Lock()\n\tdefer s.mutex.Unlock()\n\tfor targetPath, external := range externals {\n\t\tif external.TargetPath != \"\" {\n\t\t\ttargetPath = external.TargetPath\n\t\t}\n\t\tif targetPath == \"\" {\n\t\t\treturn fmt.Errorf(\"%s: empty path\", sourceAbsPath)\n\t\t}\n\n\t\texternalPath := path.Clean(targetPath)\n\t\tif strings.HasPrefix(externalPath, \"/\") || filepath.IsAbs(externalPath) {\n\t\t\treturn fmt.Errorf(\"%s: %s: path is not relative\", sourceAbsPath, targetPath)\n\t\t}\n\t\tswitch relPath, err := filepath.Rel(\".\", externalPath); {\n\t\tcase err != nil:\n\t\t\treturn fmt.Errorf(\"%s: %s: %w\", sourceAbsPath, targetPath, err)\n\t\tcase relPath == \".\":\n\t\t\treturn fmt.Errorf(\"%s: %s: empty relative path\", sourceAbsPath, targetPath)\n\t\tcase relPath == \"..\", strings.HasPrefix(relPath, \"../\"),\n\t\t\truntime.GOOS == \"windows\" && strings.HasPrefix(relPath, \"..\"+string(filepath.Separator)):\n\t\t\treturn fmt.Errorf(\"%s: %s: relative path outside target directory\", sourceAbsPath, targetPath)\n\t\t}\n\t\ttargetRelPath := parentTargetSourceRelPath.JoinString(externalPath)\n\t\texternal.sourceAbsPath = sourceAbsPath\n\t\ts.externals[targetRelPath] = append(s.externals[targetRelPath], &external)\n\t}\n\treturn nil\n}\n\n// addExternalDir adds all externals in externalsDirAbsPath to s.","sourceCodeStart":1441,"sourceCodeEnd":1477,"githubUrl":"https://github.com/twpayne/chezmoi/blob/f901167e4685db90da56d6a2a19df642cb3e0247/internal/chezmoi/sourcestate.go#L1441-L1477","documentation":"External target paths must be relative to the target (home) directory. After cleaning, if the path starts with '/' or is absolute (filepath.IsAbs, relevant on Windows e.g. C:\\...), chezmoi rejects it because externals may only write inside the target directory.","triggerScenarios":"An external entry key or TargetPath like '/opt/tool' or 'C:/tools/tool' in .chezmoiexternal, or a template rendering to an absolute path.","commonSituations":"Users copying install paths from documentation (which often use absolute paths) into external configs; cross-platform externals using Windows absolute paths.","solutions":["Change the external target path to a path relative to your home directory (e.g. '.local/bin/tool', 'dot_config/tool').","If the external must land outside home, unpack via a script external (type = 'script') that installs to the absolute location itself.","Fix the template variable that renders the absolute path."],"exampleFix":"// before\n[\"/opt/tool\"]\ntype = \"archive\"\nurl = \"https://example.com/tool.tgz\"\n// after\n[\".local/opt/tool\"]\ntype = \"archive\"\nurl = \"https://example.com/tool.tgz\"","handlingStrategy":"validation","validationCode":"target := keyOrTargetPath\nif strings.HasPrefix(target, \"/\") || filepath.IsAbs(target) {\n  return fmt.Errorf(\"external path %q must be relative to home\", target)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Express all external targets relative to $HOME","Use script-type externals for out-of-home installs","Cross-check Windows configs for drive-letter paths"],"tags":["chezmoi","external","path-validation"],"backgroundTag":"absolute-path-not-allowed","analyzedSha":"f901167e4685db90da56d6a2a19df642cb3e0247","analyzedAt":"2026-09-01T18:16:41.508Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T01:17:15.007Z"}