{"record":{"id":"293e6cd099a43c4d","repo":"dagger/dagger","slug":"q-is-not-a-directory-293e6c","errorCode":null,"errorMessage":"%q is not a directory","messagePattern":"%q is not a directory","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/cmd/dagger/shell_fs.go","lineNumber":433,"sourceCode":"\t\th.mu.RLock()\n\t\tcontext = h.wd.Context\n\t\tdigest = h.wd.Module\n\t\th.mu.RUnlock()\n\t}\n\n\t// initial context, without a loaded module (core API only)\n\tif context == nil {\n\t\t// a few quick checks first\n\t\troot, err := pathutil.Abs(subpath)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tinfo, err := os.Stat(root)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif !info.IsDir() {\n\t\t\treturn nil, fmt.Errorf(\"%q is not a directory\", root)\n\t\t}\n\n\t\tif !h.noModule {\n\t\t\t// ask API where the context dir is (.git)\n\t\t\tctx, span := Tracer().Start(ctx, \"looking for context directory\", telemetry.Internal())\n\t\t\tdefer telemetry.EndWithCause(span, &rerr)\n\n\t\t\tsrc := h.dag.ModuleSource(root, dagger.ModuleSourceOpts{\n\t\t\t\tDisableFindUp:  true,\n\t\t\t\tAllowNotExists: true,\n\t\t\t})\n\t\t\troot, err = src.LocalContextDirectoryPath(ctx)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tsubpath, err = src.SourceRootSubpath(ctx)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err","sourceCodeStart":415,"sourceCodeEnd":451,"githubUrl":"https://github.com/dagger/dagger/blob/82ba2681dbe30d3547a1dc50ea495900ab5b6047/internal/cmd/dagger/shell_fs.go#L415-L451","documentation":"newWorkdir validates that the resolved workdir root (from --workdir or cwd) is an existing directory before initializing the interactive shell's filesystem view. If os.Stat succeeds but the target is a regular file (or other non-directory), Dagger returns this error instead of starting the shell. It guards the shell against operating on paths it cannot traverse as a directory tree.","triggerScenarios":"Running 'dagger' shell / 'dagger terminal'-style init with --workdir pointing to a file (e.g. --workdir ./dagger.config.ts), or when the cwd path resolves to a file; also reached via ChangeDir into a non-directory path.","commonSituations":"Typo where a filename is passed instead of its parent directory; a symlink resolving to a file; scripts that compute the workdir path and accidentally append a filename; workdir deleted and replaced by a file.","solutions":["Pass a directory path to --workdir (or run from a directory), not a file","Verify with 'ls -la <path>' or 'os.Stat' that the target is a directory","If a symlink is involved, confirm it resolves to a directory","Update the calling script (Initialize/ChangeDir input) to strip the filename component"],"exampleFix":"// before\ndagger --workdir ./config.yaml\n\n// after\ndagger --workdir .","handlingStrategy":"validation","validationCode":"info, err := os.Stat(root)\nif err != nil { return err }\nif !info.IsDir() { return fmt.Errorf(\"%q is not a directory\", root) }\n// only then: dagger --workdir root","typeGuard":null,"tryCatchPattern":"if err := runDaggerShell(); err != nil {\n  if strings.Contains(err.Error(), \"is not a directory\") {\n    // fix workdir path and retry\n  }\n}","preventionTips":["Validate workdir with os.Stat/IsDir before launching the shell","Don't pass file paths as --workdir","Resolve symlinks and confirm target kind","Use filepath.Dir() when a path may include a filename"],"tags":["filesystem","cli","workdir"],"backgroundTag":"path-is-not-a-directory","analyzedSha":"82ba2681dbe30d3547a1dc50ea495900ab5b6047","analyzedAt":"2026-09-05T07:21:37.930Z","contentChangedAt":"2026-09-05T07:21:37.930Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}