{"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/251596fc0de2f9528c00c224252fd073a99973f4/kernel/plugin/api_storage.go#L409-L445","documentation":"Thrown by siyuan.storage.list when os.ReadDir fails on the resolved absolute path. The OS error is wrapped with %w and rejected through the Promise. It does not fire for an empty directory (that resolves to []); only for read failures.","triggerScenarios":"await siyuan.storage.list(path) where path does not exist, is not a directory (it's a file), or the process lacks permission to open the directory.","commonSituations":"Listing before creating the directory, listing a path that was removed, or a permissions/ownership change on the storage dir.","solutions":["Create the directory first with siyuan.storage.mkdir before listing it.","Catch the rejection and treat ENOENT (no such file or directory) as an empty list.","Verify the path is a directory with siyuan.storage.stat before listing."],"exampleFix":"// before\nconst entries = await siyuan.storage.list(dir);\n\n// after\ntry {\n  return await siyuan.storage.list(dir);\n} catch (e) {\n  if (/no such file or directory/.test(String(e?.cause ?? e))) return [];\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"let st; try { st = await siyuan.storage.stat(dir); } catch { st = null; } if (!st || !st.isDir) throw new Error('list target is not a directory');","typeGuard":null,"tryCatchPattern":"try { return await siyuan.storage.list(dir); } catch (e) { const cause = String((e as any)?.cause ?? e); if (/no such file/.test(cause)) return []; throw e; }","preventionTips":["Ensure the directory exists (mkdir) before listing.","Tolerate ENOENT as an empty result when listing optional dirs.","stat() before list() when you need to branch on file-vs-dir."],"tags":["plugin","storage","filesystem","promise"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}