siyuan-note/siyuan · error

read Markdown [ ]

Error message

read Markdown [%s]: %w

What it means

SiYuan's Obsidian vault importer wraps any error that occurs while opening/reading a source Markdown file with this message, carrying the file's vault-relative path and the underlying cause. newObsidianReadUserError maps the cause to localized user-facing message 345 (or 346 when the file changed mid-import via errObsidianSourceChanged). It is thrown so users see which specific file could not be read rather than a bare OS error.

Solutions

  1. Check the wrapped cause (%w) to see if it is a permission, not-exist, or source-changed error and fix that root cause
  2. Verify the file exists and is readable at the reported path; re-sync cloud placeholders so the real content is local
  3. Close Obsidian / pause sync clients, then retry the import
  4. If errObsidianSourceChanged is the cause, re-run the import so the analysis is redone against the current file state
Defensive patterns

Strategy: try-catch

Validate before calling

if _, err := os.Stat(absPath); err != nil { /* file missing/unreadable before import */ }
if info, err := os.Stat(absPath); err == nil && info.IsDir() { /* skip non-files */ }

Try / catch

if err != nil {
    if errors.Is(err, errObsidianSourceChanged) { /* re-run import */ }
    if os.IsNotExist(errors.Unwrap(err)) { /* restore/resync the file */ }
    if os.IsPermission(errors.Unwrap(err)) { /* fix permissions */ }
}

Prevention

When it happens

Trigger: os.Open(file.AbsPath) or subsequent reads fail during importObsidianVault processing — e.g. the file was deleted between vault scan and read, permissions deny access, the path is too long, or a read race detected as errObsidianSourceChanged.

Common situations: Vault lives on a network/Cloud drive that dropped; file is locked by Obsidian or a sync client (iCloud/OneDrive placeholder files); OS permission changes; file removed by another sync tool during import.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


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

Appendix: source

Thrown at kernel/model/import_obsidian.go:953

	if err != nil {
		return nil, err
	}
	after, err := os.Stat(file.AbsPath)
	if err != nil {
		return nil, err
	}
	if before.Size() != after.Size() || !before.ModTime().Equal(after.ModTime()) {
		return nil, fmt.Errorf("%w while reading", errObsidianSourceChanged)
	}
	return data, nil
}

func newObsidianReadUserError(file *obsidianSourceFile, err error) error {
	language := 345
	if errors.Is(err, errObsidianSourceChanged) {
		language = 346
	}
	return newObsidianUserError(language, file.RelPath, fmt.Errorf("read Markdown [%s]: %w", file.RelPath, err))
}

func validateObsidianReadableFile(file *obsidianSourceFile) error {
	opened, err := os.Open(file.AbsPath)
	if err != nil {
		return err
	}
	defer opened.Close()
	buffer := make([]byte, 1)
	_, err = opened.Read(buffer)
	if err != nil && !errors.Is(err, io.EOF) {
		return err
	}
	return validateObsidianSourceMetadata(file)
}

func buildObsidianHeadingIndex(doc *obsidianDocPlan, tree *parse.Tree) {
	stack := make([]string, 6)

View on GitHub (pinned to 9f775e8a12)