siyuan-note/siyuan · error

failed to read directory: %w

Error message

failed to read directory: %w

What it means

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.

Source

Thrown at kernel/plugin/api_storage.go:427

						err = fmt.Errorf("panic during siyuan.storage.list: %v", r)
					}
					p.worker.Run(func(rt *goja.Runtime) (_ any, _ error) {
						if lo.IsNil(err) {
							if resolveErr := resolve(result); resolveErr != nil {
								logging.LogErrorf("[plugin:%s] siyuan.storage.list resolve: %v", p.Name, resolveErr)
							}
						} else {
							if rejectErr := reject(rt.NewGoError(err)); rejectErr != nil {
								logging.LogErrorf("[plugin:%s] siyuan.storage.list reject: %v", p.Name, rejectErr)
							}
						}
						return
					}, nil)
				}()

				entries, readErr := os.ReadDir(abs)
				if readErr != nil {
					err = fmt.Errorf("failed to read directory: %w", readErr)
					return
				}

				results := make([]R, 0, len(entries))
				for _, entry := range entries {
					info, infoErr := entry.Info()
					if infoErr != nil {
						continue
					}
					results = append(results, R{
						"name":      entry.Name(),
						"isDir":     info.IsDir(),
						"isSymlink": util.IsSymlink(entry),
						"updated":   info.ModTime().Unix(),
					})
				}

				result = results

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Create the directory first with siyuan.storage.mkdir before listing it.
  2. Catch the rejection and treat ENOENT (no such file or directory) as an empty list.
  3. Verify the path is a directory with siyuan.storage.stat before listing.

Example fix

// before
const entries = await siyuan.storage.list(dir);

// after
try {
  return await siyuan.storage.list(dir);
} catch (e) {
  if (/no such file or directory/.test(String(e?.cause ?? e))) return [];
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

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');

Try / catch

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; }

Prevention

When it happens

Trigger: 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.

Common situations: Listing before creating the directory, listing a path that was removed, or a permissions/ownership change on the storage dir.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/bf7e1f0b6654bef1. Report an issue: GitHub.