siyuan-note/siyuan · error
Obsidian Vault is unreadable: path is empty
Error message
Obsidian Vault is unreadable: path is empty
What it means
A wrapped variant of errObsidianVaultUnreadable (line 559-560): returned by validateObsidianVaultRoot when the localPath argument, after trimming whitespace, is the empty string. This is the first guard in root validation, catching a missing or blank path before any filesystem access. It wraps the unreadable sentinel with the suffix ': path is empty'.
Source
Thrown at kernel/model/import_obsidian.go:560
}
ret.Analysis.Warnings = append(ret.Analysis.Warnings, asset.Source.RelPath)
continue
}
ret.ImportAssets[key] = asset
ret.Analysis.ImportableAssetCount++
ret.Analysis.ImportableAssetSize += asset.Source.Size
}
ret.Analysis.UnreferencedFileCount = len(ret.ImportAssets) - len(ret.ReferencedAssets)
if ret.Analysis.UnreferencedFileCount < 0 {
ret.Analysis.UnreferencedFileCount = 0
}
progress(100, "Analysis completed")
return ret, nil
}
func validateObsidianVaultRoot(localPath string) (string, error) {
if strings.TrimSpace(localPath) == "" {
return "", fmt.Errorf("%w: path is empty", errObsidianVaultUnreadable)
}
abs, err := filepath.Abs(filepath.Clean(localPath))
if err != nil {
return "", fmt.Errorf("%w: normalize Vault path: %v", errObsidianVaultUnreadable, err)
}
info, err := os.Lstat(abs)
if err != nil {
return "", fmt.Errorf("%w: read Vault root: %v", errObsidianVaultUnreadable, err)
}
if !info.IsDir() {
return "", errObsidianVaultNotDirectory
}
if info.Mode()&os.ModeSymlink != 0 || isObsidianResolvedLink(abs) {
return "", fmt.Errorf("%w: Vault root is a symbolic link or reparse point", errObsidianVaultUnsafePath)
}
if util.IsSensitivePath(abs) {
return "", fmt.Errorf("%w: selected Vault path is sensitive", errObsidianVaultUnsafePath)
}View on GitHub (pinned to 251596fc0d)
Solutions
- Provide a non-empty directory path as localPath in the request body.
- Validate on the client that the path field is non-empty before submitting.
- Check the JSON request body includes a 'localPath' key with a real filesystem path.
- If using the UI, ensure the folder picker completed successfully before clicking import.
Example fix
// before
POST /api/import/startObsidianVaultAnalysis { "localPath": "" }
// -> "Obsidian Vault is unreadable: path is empty"
// after
POST /api/import/startObsidianVaultAnalysis { "localPath": "/home/user/MyVault" } Defensive patterns
Strategy: validation
Validate before calling
// Ensure a non-empty path before calling the API
if strings.TrimSpace(localPath) == "" {
return errors.New("Vault path must not be empty")
} Try / catch
if _, err := model.StartObsidianVaultAnalysis(localPath); err != nil {
if errors.Is(err, errObsidianVaultUnreadable) && strings.Contains(err.Error(), "path is empty") {
return errors.New("no Vault folder was provided; please select a folder")
}
} Prevention
- Validate the path field is non-empty on the client before submitting.
- Ensure the folder picker returns a value before enabling the import button.
When it happens
Trigger: POST /api/import/startObsidianVaultAnalysis with localPath set to "", a whitespace-only string, or null/undefined coerced to empty. The trim check at line 559 fires immediately.
Common situations: The frontend file picker returned no selection; the path field was left blank in a scripted API call; a null value was JSON-encoded as empty string; a UI bug submitted the form without a selected folder.
Related errors
- Obsidian Vault is unreadable
- Obsidian Vault path is not a directory
- Obsidian Vault is unreadable: normalize Vault path: %v
- Obsidian Vault is unreadable: read Vault root: %v
- import path is not sub path of import dir
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/c22b661765446c88.
Report an issue: GitHub.