{"record":{"id":"b56142eadf368fb9","repo":"wavetermdev/waveterm","slug":"failed-to-read-waveapps-directory-w","errorCode":null,"errorMessage":"failed to read waveapps directory: %w","messagePattern":"failed to read waveapps directory: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/waveappstore/waveappstore.go","lineNumber":468,"sourceCode":"\n\tif _, err := os.Stat(appDir); os.IsNotExist(err) {\n\t\treturn nil, fmt.Errorf(\"app directory does not exist: %s\", appDir)\n\t}\n\n\treturn fileutil.ReadDirRecursive(appDir, 10000)\n}\n\nfunc ListAllApps() ([]wshrpc.AppInfo, error) {\n\thomeDir := wavebase.GetHomeDir()\n\twaveappsDir := filepath.Join(homeDir, \"waveapps\")\n\n\tif _, err := os.Stat(waveappsDir); os.IsNotExist(err) {\n\t\treturn []wshrpc.AppInfo{}, nil\n\t}\n\n\tnamespaces, err := os.ReadDir(waveappsDir)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to read waveapps directory: %w\", err)\n\t}\n\n\tvar appInfos []wshrpc.AppInfo\n\n\tfor _, ns := range namespaces {\n\t\tif !ns.IsDir() {\n\t\t\tcontinue\n\t\t}\n\n\t\tnamespace := ns.Name()\n\t\tnsPath := filepath.Join(waveappsDir, namespace)\n\n\t\tapps, err := os.ReadDir(nsPath)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tfor _, app := range apps {","sourceCodeStart":450,"sourceCodeEnd":486,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/waveappstore/waveappstore.go#L450-L486","documentation":"ListAllApps reads the ~/waveapps root directory to enumerate namespaces. If os.ReadDir on that root fails for any reason other than it not existing (which is handled as an empty list), the error is wrapped as \"failed to read waveapps directory\". This signals filesystem-level trouble with the waveapps root itself.","triggerScenarios":"os.ReadDir(waveappsDir) fails with EACCES (no read permission on ~/waveapps), ELOOP (waveapps is a symlink loop), or an I/O error on a mounted volume — anything except a clean 'does not exist'.","commonSituations":"~/waveapps created by another user or root with restrictive permissions; waveapps replaced by a file or broken symlink; network/external drive hosting waveapps that got disconnected mid-listing.","solutions":["Check permissions on ~/waveapps (user needs read + execute): ls -ld ~/waveapps","Confirm ~/waveapps is a real directory, not a file or broken symlink; recreate if needed","Fix ownership: chown -R \"$USER\" ~/waveapps","If it is on an external mount, remount the volume and retry"],"exampleFix":"// shell diagnosis\nls -ld ~/waveapps\n// fix\nsudo chown \"$USER\" ~/waveapps && chmod 755 ~/waveapps","handlingStrategy":"try-catch","validationCode":"waveappsDir := filepath.Join(homeDir, \"waveapps\")\nif fi, err := os.Stat(waveappsDir); err == nil && (!fi.IsDir() || unix.Access(waveappsDir, unix.R_OK) != nil) {\n\treturn fmt.Errorf(\"waveapps dir missing, not a directory, or unreadable: %s\", waveappsDir)\n}","typeGuard":null,"tryCatchPattern":"apps, err := ListAllApps()\nif err != nil {\n\tif strings.Contains(err.Error(), \"failed to read waveapps directory\") {\n\t\treturn fmt.Errorf(\"cannot access ~/waveapps: %w (check permissions/symlink)\", err)\n\t}\n\treturn err\n}","preventionTips":["Keep ~/waveapps as a plain directory owned by the running user (755)","Do not replace waveapps with a file or symlink loop; restore it if deleted","Monitor mounts if waveapps lives on external/network storage"],"tags":["filesystem","permissions","go"],"backgroundTag":"readdir-failed","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}