siyuan-note/siyuan · error

Obsidian Vault has no readable Markdown

Error message

Obsidian Vault has no readable Markdown

What it means

`errObsidianVaultMarkdownMissing` is returned when the Vault passes structural validation (directory, `.obsidian` present) but the walk found zero readable `.md` files. It maps to i18n key 340, telling the user there is nothing to import.

Solutions

  1. Confirm the Vault actually contains `.md` notes and that they are readable by the current OS user
  2. Rename notes with other extensions (`.txt`, `.mdown`) to `.md` inside the Vault
  3. Choose a different Vault folder that holds the notes

Example fix

// before
// vault with only images and .txt files
scanObsidianVault(...) // errObsidianVaultMarkdownMissing
// after
// rename .txt notes to .md inside the vault, then import
scanObsidianVault(...) // imports normally
Defensive patterns

Strategy: validation

Validate before calling

const md = (await fs.readdir(vaultPath, { recursive: true }))
  .filter(f => f.toLowerCase().endsWith(".md"));
if (md.length === 0) {
  throw new Error("Vault contains no .md notes; nothing to import");
}

Type guard

function hasMarkdownNotes(files: string[]): boolean {
  return files.some(f => f.toLowerCase().endsWith(".md"));
}

Try / catch

try {
  await api.importObsidianVault(vaultPath);
} catch (e) {
  if (isVaultMarkdownMissing(e)) {
    // inform user the vault has no importable Markdown and stop
  }
}

Prevention

When it happens

Trigger: During Vault scan, `len(mdFiles) == 0` after collecting Markdown files — no `.md` files matched, or all candidate files failed readability checks during enumeration.

Common situations: Vault contains only attachments/canvas/other extensions; notes were renamed to `.txt`; permissions or path filters excluded every note; an empty new vault was selected.

Understand the failure class

Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/3c0e285040a6da75. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/import_obsidian.go:234

}

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) {
				obsidianTasksMu.Unlock()
				return nil, errors.New(Conf.Language(329))
			}
			if active.Cancel != nil {

View on GitHub (pinned to 9f775e8a12)