sipeed/picoclaw · error
duplicate expose_path target: %s
Error message
duplicate expose_path target: %s
What it means
ValidateExposePaths tracks cleaned target paths in a set; a second expose_paths entry resolving to the same target (explicitly or by defaulting to its source) is rejected. Duplicate mount targets would make the mapping order-dependent, so preflight fails with the colliding path.
Source
Thrown at pkg/isolation/runtime.go:184
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{
Source: source,
Target: filepath.Clean(target),
Mode: item.Mode,View on GitHub (pinned to 49183d7e8d)
Solutions
- Remove or merge the duplicate rules so each target appears once
- If two sources must both be visible, give them distinct targets (e.g. /workspace/a and /workspace/b)
- Normalize paths first: remember comparison happens on filepath.Clean values, so cosmetic differences do not make entries distinct
Example fix
# before
isolation:
expose_paths:
- {source: /home/me/project, mode: rw}
- {source: /home/me/project, target: /project, mode: ro}
# after
isolation:
expose_paths:
- {source: /home/me/project, mode: rw} Defensive patterns
Strategy: validation
Validate before calling
seen := map[string]struct{}{}
for _, p := range cfg.Isolation.ExposePaths {
target := p.Target
if target == "" {
target = p.Source
}
target = filepath.Clean(target)
if _, dup := seen[target]; dup {
return fmt.Errorf("duplicate expose target %s — merge or split targets", target)
}
seen[target] = struct{}{}
} Prevention
- Dedupe on cleaned target paths whenever merging config layers that add expose rules
- Prefer mapping several sources to distinct subdirectory targets instead of one shared target
When it happens
Trigger: Two entries with the same target; or two entries whose target keys are empty so both default to their (identical) source paths; entries that differ in textual form but Clean to the same path (e.g. /a/b and /a//b/).
Common situations: Appending a new rule without noticing an existing one mounts the same location; YAML anchors or copy-paste duplication; merging config layers that each add a rule for the same directory.
Related errors
- source is required
- invalid expose_paths mode: %s
- source and target must be absolute paths
- ${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/d56efa5a990dacf3.
Report an issue: GitHub.