sipeed/picoclaw · error
source and target must be absolute paths
Error message
source and target must be absolute paths
What it means
After filepath.Clean (and defaulting target to source when target is empty), ValidateExposePaths requires both source and target to be absolute paths. Relative paths — including "~/..." which Clean does not expand — make the exposure rule ambiguous against the child's redirected filesystem, so preflight rejects them.
Source
Thrown at pkg/isolation/runtime.go:181
func ValidateExposePaths(items []config.ExposePath) error {
seen := map[string]struct{}{}
for _, item := range items {
if item.Source == "" {
return fmt.Errorf("source is required")
}
if item.Mode != "ro" && item.Mode != "rw" {
return fmt.Errorf("invalid expose_paths mode: %s", item.Mode)
}
source := filepath.Clean(item.Source)
target := item.Target
if target == "" {
target = source
}
target = filepath.Clean(target)
if !filepath.IsAbs(source) || !filepath.IsAbs(target) {
return fmt.Errorf("source and target must be absolute paths")
}
if _, ok := seen[target]; ok {
return fmt.Errorf("duplicate expose_path target: %s", target)
}
seen[target] = struct{}{}
}
return nil
}
// NormalizeExposePath fills implicit defaults and cleans path values so merge
// and validation logic can work with canonical paths.
func NormalizeExposePath(item config.ExposePath) config.ExposePath {
source := filepath.Clean(item.Source)
target := item.Target
if target == "" {
target = source
}
return config.ExposePath{View on GitHub (pinned to 49183d7e8d)
Solutions
- Expand ~ yourself and write the full absolute path (e.g. /home/me/project)
- Give every entry an explicit absolute target when it differs from the source
- On Linux, ensure paths start with /; on Windows, use drive-letter or UNC absolute paths
- If generating config programmatically, run filepath.Abs on values before serializing
Example fix
# before
isolation:
expose_paths:
- source: ~/project
mode: ro
# after
isolation:
expose_paths:
- source: /home/me/project
mode: ro Defensive patterns
Strategy: validation
Validate before calling
for _, p := range cfg.Isolation.ExposePaths {
src := p.Source
if strings.HasPrefix(src, "~" + string(os.PathSeparator)) || src == "~" {
if home, err := os.UserHomeDir(); err == nil {
src = filepath.Join(home, strings.TrimPrefix(src, "~"))
}
}
if !filepath.IsAbs(src) {
abs, err := filepath.Abs(src)
if err != nil {
return fmt.Errorf("cannot make expose source absolute: %s", p.Source)
}
src = abs
}
p.Source = src
// repeat for target, then validate
} Prevention
- Expand ~ and make paths absolute in your config loader before they reach isolation
- Remember absolute means leading / on unix and drive/UNC paths on windows — write per-OS examples in docs
When it happens
Trigger: source: ~/project (tilde is a relative path to filepath.IsAbs); source: ./data or data; target: workspace (relative); a Windows-style path like C:\data evaluated on Linux where filepath.IsAbs expects a leading /.
Common situations: Shell-style habits (~) carried into config; configs moved between Windows and Linux hosts where the absoluteness rules differ; relative paths that worked in a non-isolated mode and were never made absolute.
Related errors
- source is required
- invalid expose_paths mode: %s
- duplicate expose_path target: %s
- ${label} must be a JSON object.
- ${label}.${key} must be a string.
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/ad690f505f3aaa6c.
Report an issue: GitHub.