{"record":{"id":"b022fba6c2fee9a2","repo":"sipeed/picoclaw","slug":"source-is-required","errorCode":null,"errorMessage":"source is required","messagePattern":"source is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/isolation/runtime.go","lineNumber":167,"sourceCode":"\t\tenvMap[\"XDG_CONFIG_HOME\"] = userEnv.Config\n\t\tenvMap[\"XDG_CACHE_HOME\"] = userEnv.Cache\n\t\tenvMap[\"XDG_STATE_HOME\"] = userEnv.State\n\t}\n\n\tenv := make([]string, 0, len(envMap))\n\tfor k, v := range envMap {\n\t\tenv = append(env, fmt.Sprintf(\"%s=%s\", k, v))\n\t}\n\tcmd.Env = env\n}\n\n// ValidateExposePaths verifies the user-supplied path exposure rules before a\n// child process is started.\nfunc ValidateExposePaths(items []config.ExposePath) error {\n\tseen := map[string]struct{}{}\n\tfor _, item := range items {\n\t\tif item.Source == \"\" {\n\t\t\treturn fmt.Errorf(\"source is required\")\n\t\t}\n\t\tif item.Mode != \"ro\" && item.Mode != \"rw\" {\n\t\t\treturn fmt.Errorf(\"invalid expose_paths mode: %s\", item.Mode)\n\t\t}\n\n\t\tsource := filepath.Clean(item.Source)\n\t\ttarget := item.Target\n\t\tif target == \"\" {\n\t\t\ttarget = source\n\t\t}\n\t\ttarget = filepath.Clean(target)\n\n\t\tif !filepath.IsAbs(source) || !filepath.IsAbs(target) {\n\t\t\treturn fmt.Errorf(\"source and target must be absolute paths\")\n\t\t}\n\t\tif _, ok := seen[target]; ok {\n\t\t\treturn fmt.Errorf(\"duplicate expose_path target: %s\", target)\n\t\t}","sourceCodeStart":149,"sourceCodeEnd":185,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/isolation/runtime.go#L149-L185","documentation":"ValidateExposePaths rejects any isolation.expose_paths entry whose Source is the empty string. Source identifies the host path to expose into the isolated child, so an entry without it is meaningless and preflight fails fast instead of launching a child with a silently ignored rule.","triggerScenarios":"A config entry like {target: \"/data\", mode: \"ro\"} with source omitted, or source: \"\" explicitly; commonly a typo where source/target fields are swapped or a template placeholder was never filled in.","commonSituations":"Hand-edited YAML/JSON isolation config; config generated from a template where the source variable was empty; merging configs where the source key was dropped.","solutions":["Set a non-empty, absolute source path on every expose_paths entry","If the entry was accidental, delete it instead of leaving an empty rule","Double-check field order/names — a target-only entry means the source was probably lost during editing"],"exampleFix":"# before\nisolation:\n  expose_paths:\n    - target: /workspace\n      mode: rw\n\n# after\nisolation:\n  expose_paths:\n    - source: /home/me/project\n      target: /workspace\n      mode: rw","handlingStrategy":"validation","validationCode":"for _, p := range cfg.Isolation.ExposePaths {\n    if strings.TrimSpace(p.Source) == \"\" {\n        return fmt.Errorf(\"expose_paths entry with target %q is missing source\", p.Target)\n    }\n}","typeGuard":null,"tryCatchPattern":"if err := isolation.ValidateExposePaths(cfg.Isolation.ExposePaths); err != nil {\n    return fmt.Errorf(\"isolation config rejected at load time: %w\", err)\n}","preventionTips":["Validate expose_paths in your own config loader before the app starts, not at first child launch","Template-generate configs with a non-empty check on source, and reject partially-filled entries"],"tags":["config","validation","expose-paths"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}