{"record":{"id":"ccb6052eca7e8494","repo":"wavetermdev/waveterm","slug":"reading-directory-s-w","errorCode":null,"errorMessage":"reading directory %s: %w","messagePattern":"reading directory (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/wsh/cmd/wshcmd-ai.go","lineNumber":120,"sourceCode":"\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"reading from stdin: %w\", err)\n\t\t\t}\n\t\t\tfileName = \"stdin\"\n\t\t\tmimeType = \"text/plain\"\n\t\t} else {\n\t\t\tfileInfo, err := os.Stat(filePath)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"accessing file %s: %w\", filePath, err)\n\t\t\t}\n\t\t\tabsPath, err := filepath.Abs(filePath)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"getting absolute path for %s: %w\", filePath, err)\n\t\t\t}\n\n\t\t\tif fileInfo.IsDir() {\n\t\t\t\tresult, err := fileutil.ReadDir(filePath, 500)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"reading directory %s: %w\", filePath, err)\n\t\t\t\t}\n\t\t\t\tjsonData, err := json.Marshal(result)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"marshaling directory listing for %s: %w\", filePath, err)\n\t\t\t\t}\n\t\t\t\tdata = jsonData\n\t\t\t\tfileName = absPath\n\t\t\t\tmimeType = \"directory\"\n\t\t\t} else {\n\t\t\t\tdata, err = os.ReadFile(filePath)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"reading file %s: %w\", filePath, err)\n\t\t\t\t}\n\t\t\t\tfileName = absPath\n\t\t\t\tmimeType = detectMimeType(data)\n\t\t\t}\n\t\t}\n","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/cmd/wsh/cmd/wshcmd-ai.go#L102-L138","documentation":"Wraps fileutil.ReadDir(filePath, 500) failure when a `wsh ai` argument is a directory. The command builds a JSON directory listing (max 500 entries) instead of reading file contents. This error means the directory listing failed — permission denied, the path disappeared between Stat and ReadDir, or it is not actually a readable directory.","triggerScenarios":"`wsh ai <dir>` where the directory is unreadable (no r permission), the directory was removed between os.Stat and ReadDir (race), or a special filesystem returns errors on readdir.","commonSituations":"Attaching /root or another protected directory as a normal user; attaching a directory on a failing mount/NFS; scripts where the directory is deleted concurrently.","solutions":["Check directory readability: `ls <dir>`; fix permissions with chmod/chown if needed.","Verify the directory still exists right before running (guards against races in scripts).","Attach specific files from the directory instead of the directory itself.","If the listing would exceed 500 entries, attach a narrower subdirectory or a filtered file list."],"exampleFix":"// before\n$ wsh ai /var/log/private\n// after\n$ sudo chmod o+rx /var/log/private  # or attach readable files instead\n$ wsh ai /var/log/private/app.log","handlingStrategy":"validation","validationCode":"if [ -d \"$dir\" ] && ! [ -r \"$dir\" ] && ! [ -x \"$dir\" ]; then\n  echo \"directory not readable: $dir\" >&2\n  exit 1\nfi\nentry_count=$(ls -1 \"$dir\" | wc -l)\n[ \"$entry_count\" -le 500 ] || echo \"warning: $dir has $entry_count entries; listing truncated at 500\"","typeGuard":null,"tryCatchPattern":"if err := runAI(args); err != nil {\n    var perr *fs.PathError\n    if errors.As(err, &perr) && errors.Is(perr.Err, syscall.EACCES) {\n        return fmt.Errorf(\"cannot read directory %q: fix permissions or attach individual files\", perr.Path)\n    }\n    return err\n}","preventionTips":["Verify `ls <dir>` works as the same user before attaching a directory.","Attach specific files instead of broad/system directories.","Guard scripts against concurrent deletion of the directory (re-check existence just before the call)."],"tags":["filesystem","directory","permissions","wsh"],"backgroundTag":"directory-read-permission-denied","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}