{"record":{"id":"97c6372cd3077992","repo":"plandex-ai/plandex","slug":"error-creating-directory-v-97c637","errorCode":null,"errorMessage":"error creating directory: %v","messagePattern":"error creating directory: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/server/db/diff_helpers.go","lineNumber":66,"sourceCode":"\thasAnyOriginal := false\n\n\tfor path, context := range planState.ContextsByPath {\n\t\tgo func(path string, context *shared.Context) {\n\t\t\tdefer func() {\n\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\tlog.Printf(\"panic in GetPlanDiffs: %v\\n%s\", r, debug.Stack())\n\t\t\t\t\terrCh <- fmt.Errorf(\"panic in GetPlanDiffs: %v\\n%s\", r, debug.Stack())\n\t\t\t\t\truntime.Goexit() // don't allow outer function to continue and double-send to channel\n\t\t\t\t}\n\t\t\t}()\n\t\t\t_, hasPath := files[path]\n\t\t\t_, hasRemoved := removed[path]\n\t\t\tif hasPath || hasRemoved {\n\t\t\t\thasAnyOriginal = true\n\t\t\t\t// ensure file directory exists\n\t\t\t\terr = os.MkdirAll(filepath.Dir(filepath.Join(tempDirPath, path)), 0755)\n\t\t\t\tif err != nil {\n\t\t\t\t\terrCh <- fmt.Errorf(\"error creating directory: %v\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\terr = os.WriteFile(filepath.Join(tempDirPath, path), []byte(context.Body), 0644)\n\t\t\t\tif err != nil {\n\t\t\t\t\terrCh <- fmt.Errorf(\"error writing file: %v\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\terrCh <- nil\n\t\t}(path, context)\n\t}\n\n\tfor range planState.ContextsByPath {\n\t\terr = <-errCh\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"error writing original files to temp dir: %v\", err)\n\t\t}","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/db/diff_helpers.go#L48-L84","documentation":"Inside the per-path goroutine, os.MkdirAll ensures the subdirectory for a plan file exists in the temp repo before the file body is written. If MkdirAll fails, the error is sent on errCh and the goroutine returns early; GetPlanDiffs wraps it as 'error writing original files to temp dir: error creating directory: ...'.","triggerScenarios":"os.MkdirAll(filepath.Dir(filepath.Join(tempDirPath, path))) fails because the temp dir vanished, permissions are wrong, the path component is invalid (e.g. illegal characters on the fs), or disk is full.","commonSituations":"Temp dir cleaned up concurrently or tmpwatch removing it mid-run; file paths containing invalid segments (leading '/', '..' traversal, NUL bytes) from plan state; read-only org volume; inode/space exhaustion.","solutions":["Inspect the wrapped os error (path + syscall errno) in the message; verify that exact path is creatable as the server user.","Sanitize/normalize file paths from plan state before joining (reject absolute paths, '..' segments, NUL).","Ensure the org/temp directory is writable and not deleted during the run (disable aggressive tmp cleaners).","Free disk space / check inode usage if errors are ENOSPC."],"exampleFix":"// before\nerr = os.MkdirAll(filepath.Dir(filepath.Join(tempDirPath, path)), 0755)\n// after (sanitize first)\nclean := filepath.Clean(path)\nif filepath.IsAbs(clean) || strings.HasPrefix(clean, \"..\") {\n    errCh <- fmt.Errorf(\"invalid plan path: %s\", path)\n    return\n}\nerr = os.MkdirAll(filepath.Dir(filepath.Join(tempDirPath, clean)), 0755)","handlingStrategy":"validation","validationCode":"func isSafeRelPath(p string) bool {\n    if p == \"\" || filepath.IsAbs(p) || strings.Contains(p, \"\\x00\") {\n        return false\n    }\n    for _, part := range strings.Split(filepath.ToSlash(p), \"/\") {\n        if part == \"..\" {\n            return false\n        }\n    }\n    return true\n}","typeGuard":null,"tryCatchPattern":"out, err := GetPlanDiffs(orgId, planId, plain)\nif err != nil && strings.Contains(err.Error(), \"error creating directory\") {\n    // inspect disk/permissions and retry once after checking temp dir exists\n    if _, statErr := os.Stat(getOrgDir(orgId)); statErr != nil {\n        log.Printf(\"org dir gone: %v\", statErr)\n    }\n}","preventionTips":["Sanitize all plan file paths (reject absolute paths, '..', NUL) before filesystem use.","Exclude temp-dir trees from aggressive tmpwatch/systemd-tmpfiles cleanup during runs.","Monitor disk space and permissions on the org data volume.","Add a pre-flight writability check (create+remove a probe file) before diff computation."],"tags":["filesystem","os","mkdir"],"backgroundTag":"mkdir-failed","analyzedSha":"e2d772072efadbe41d2946d97d79be55532dbab5","analyzedAt":"2026-09-05T20:56:53.631Z","contentChangedAt":"2026-09-05T20:56:53.631Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}