{"record":{"id":"bf7e1f0b6654bef1","repo":"siyuan-note/siyuan","slug":"failed-to-read-directory-w","errorCode":null,"errorMessage":"failed to read directory: %w","messagePattern":"failed to read directory: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/plugin/api_storage.go","lineNumber":427,"sourceCode":"\t\t\t\t\t\terr = fmt.Errorf(\"panic during siyuan.storage.list: %v\", r)\n\t\t\t\t\t}\n\t\t\t\t\tp.worker.Run(func(rt *goja.Runtime) (_ any, _ error) {\n\t\t\t\t\t\tif lo.IsNil(err) {\n\t\t\t\t\t\t\tif resolveErr := resolve(result); resolveErr != nil {\n\t\t\t\t\t\t\t\tlogging.LogErrorf(\"[plugin:%s] siyuan.storage.list resolve: %v\", p.Name, resolveErr)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif rejectErr := reject(rt.NewGoError(err)); rejectErr != nil {\n\t\t\t\t\t\t\t\tlogging.LogErrorf(\"[plugin:%s] siyuan.storage.list reject: %v\", p.Name, rejectErr)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn\n\t\t\t\t\t}, nil)\n\t\t\t\t}()\n\n\t\t\t\tentries, readErr := os.ReadDir(abs)\n\t\t\t\tif readErr != nil {\n\t\t\t\t\terr = fmt.Errorf(\"failed to read directory: %w\", readErr)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tresults := make([]R, 0, len(entries))\n\t\t\t\tfor _, entry := range entries {\n\t\t\t\t\tinfo, infoErr := entry.Info()\n\t\t\t\t\tif infoErr != nil {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tresults = append(results, R{\n\t\t\t\t\t\t\"name\":      entry.Name(),\n\t\t\t\t\t\t\"isDir\":     info.IsDir(),\n\t\t\t\t\t\t\"isSymlink\": util.IsSymlink(entry),\n\t\t\t\t\t\t\"updated\":   info.ModTime().Unix(),\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\tresult = results","sourceCodeStart":409,"sourceCodeEnd":445,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/plugin/api_storage.go#L409-L445","documentation":"os.ReadDir failed for the resolved path in siyuan.storage.list. The OS error is wrapped and the Promise is rejected with 'failed to read directory: <cause>'. Common causes are the path not existing or not being a directory (ENOENT, ENOTDIR) or lacking read permission.","triggerScenarios":"Calling siyuan.storage.list(path) where path was never created (e.g. before the first storage.put), points to a file instead of a directory, or the directory cannot be read due to permissions.","commonSituations":"Listing a subdirectory before any file was written into it; a typo in the path; assuming list works on file paths; directories deleted externally by sync or the user.","solutions":["Create the directory first (e.g. write a file with siyuan.storage.put) or treat ENOENT as an empty result","Ensure the path is a directory, not a file","Check the wrapped OS error to distinguish not-found from permission problems","Wrap the call in try/catch and fall back to an empty list for optional directories"],"exampleFix":"// before\nconst entries = await siyuan.storage.list(dir);\n\n// after\nlet entries = [];\ntry {\n  entries = await siyuan.storage.list(dir);\n} catch (e) {\n  if (!String(e).includes(\"no such file\")) throw e;\n}","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"let entries = [];\ntry {\n  entries = await siyuan.storage.list(dir);\n} catch (e) {\n  if (String(e).includes(\"no such file\")) {\n    entries = []; // treat missing dir as empty\n  } else throw e;\n}","preventionTips":["Create the directory (via storage.put) before listing it","Only pass directory paths to storage.list, never file paths","Treat missing directories as empty results in optional-data flows"],"tags":["plugin","storage","filesystem","directory"],"backgroundTag":"directory-not-found","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}