siyuan-note/siyuan · error
Obsidian Vault is unreadable
Error message
Obsidian Vault is unreadable
What it means
A sentinel base error used as the wrapping parent for any condition that makes the Obsidian Vault unreadable at a filesystem level. It is never returned bare to the user; instead it is wrapped with a descriptive suffix (e.g. 'path is empty', 'normalize Vault path', 'read Vault root', 'read Vault config directory') via fmt.Errorf with %w. The obsidianVaultErrorLanguage helper maps this sentinel to i18n key 336 for user-facing display. It is the root cause for errors 774, 775, 776, and the config-read failure branch.
Source
Thrown at kernel/model/import_obsidian.go:230
}
func (err *obsidianUserError) Error() string {
return err.Cause.Error()
}
func (err *obsidianUserError) Unwrap() error {
return err.Cause
}
func newObsidianUserError(detailLanguage int, relPath string, cause error) error {
return &obsidianUserError{DetailLanguage: detailLanguage, RelPath: relPath, Cause: cause}
}
var (
obsidianTasksMu sync.Mutex
obsidianTasks = map[string]*obsidianTask{}
obsidianActive string
errObsidianVaultUnreadable = errors.New("Obsidian Vault is unreadable")
errObsidianVaultNotDirectory = errors.New("Obsidian Vault path is not a directory")
errObsidianVaultUnsafePath = errors.New("Obsidian Vault path is unsafe")
errObsidianVaultConfigMissing = errors.New("Obsidian Vault config directory is missing")
errObsidianVaultMarkdownMissing = errors.New("Obsidian Vault has no readable Markdown")
errObsidianSourceChanged = errors.New("Obsidian source file changed")
obsidianBlockIDPattern = regexp.MustCompile(`(?m)(?:^|[ \t])\^([A-Za-z0-9-]+)[ \t]*$`)
obsidianQuotePattern = regexp.MustCompile(`^((?:[ \t]*>[ \t]?)+)(.*)$`)
obsidianListItemPattern = regexp.MustCompile(`^([ \t]*(?:[-+*]|\d+[.)])[ \t]+)(.*)$`)
obsidianFootnotePattern = regexp.MustCompile(`(?m)\[\^[^\]\r\n]+\]`)
)
func StartObsidianVaultAnalysis(localPath string) (*ObsidianVaultTask, error) {
var replacedTaskID string
obsidianTasksMu.Lock()
if obsidianActive != "" {
if active := obsidianTasks[obsidianActive]; active != nil && !isObsidianTerminalState(active.State) {
if !isObsidianPreImportState(active.State) {View on GitHub (pinned to 251596fc0d)
Solutions
- Verify the localPath passed to startObsidianVaultAnalysis is non-empty and points to an existing, readable directory.
- Check filesystem permissions on the Vault root and ensure the SiYuan process can stat it.
- If the path involves a network share or removable drive, confirm it is mounted and accessible, then retry analysis.
- Inspect the wrapped suffix in the error (after the colon) — it names the exact failing step (empty path, normalize, read root, config read).
Example fix
// before
abs, err := filepath.Abs(filepath.Clean(localPath))
if err != nil {
return "", fmt.Errorf("%w: normalize Vault path: %v", errObsidianVaultUnreadable, err)
}
// after: caller validates and gives a clear path before calling the API
if strings.TrimSpace(localPath) == "" {
return fmt.Errorf("Vault path must not be empty")
}
// then call startObsidianVaultAnalysis(localPath) Defensive patterns
Strategy: validation
Validate before calling
// Pre-validate the Vault path is non-empty and resolvable before calling the API
func preflightVaultPath(localPath string) error {
if strings.TrimSpace(localPath) == "" { return errors.New("Vault path is empty") }
abs, err := filepath.Abs(filepath.Clean(localPath))
if err != nil { return fmt.Errorf("cannot resolve path: %w", err) }
if _, err := os.Lstat(abs); err != nil { return fmt.Errorf("cannot access path: %w", err) }
return nil
} Try / catch
task, err := model.StartObsidianVaultAnalysis(localPath)
if err != nil {
if errors.Is(err, errObsidianVaultUnreadable) {
// show the wrapped suffix to the user as an actionable filesystem error
return fmt.Errorf("Vault cannot be read (%s). Check the path exists and is accessible", err)
}
return err
} Prevention
- Always pass an absolute, existing directory path to startObsidianVaultAnalysis.
- Confirm the Vault directory and .obsidian config are readable by the SiYuan process.
- Run a preflight stat on the path in the caller before invoking the API.
When it happens
Trigger: Calling POST /api/import/startObsidianVaultAnalysis with a localPath that is empty, cannot be converted to an absolute path, does not exist on disk (os.Lstat fails), or whose .obsidian config directory exists but cannot be stat'd. validateObsidianVaultRoot wraps this sentinel in each of those branches.
Common situations: Passing an empty string as localPath; a path containing characters invalid for filepath.Abs on the host OS; the Vault directory was deleted or moved between selection and analysis; permissions deny Lstat on the Vault root or its config dir; a network/UNC path that is unreachable.
Related errors
- Obsidian Vault path is not a directory
- Obsidian Vault path is unsafe
- Obsidian Vault config directory is missing
- Obsidian Vault has no readable Markdown
- Obsidian source file changed
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/2a28a1e7003272ae.
Report an issue: GitHub.