siyuan-note/siyuan · warning

346

346

Error message

Markdown [%s] changed after analysis: %w

What it means

Just before converting each document, the importer re-validates the source file's metadata (size/mtime via validateObsidianSourceMetadata) against what was recorded at analysis time. Any drift is wrapped as localized user error 346, 'Markdown [%s] changed after analysis', naming the file — the file's content changed after the import plan was built, so the import is aborted rather than importing a stale or mismatched document.

Solutions

  1. Re-run the import so the analysis captures the file's current state
  2. Close Obsidian and pause sync during import to prevent concurrent writes
  3. If only mtimes changed (touch by a tool) with identical content, re-running analysis resolves it; do not attempt to bypass the check
Defensive patterns

Strategy: retry

Validate before calling

// compare current size+mtime of each source file against the values captured at analysis time
for _, s := range analyzedSources {
    if info, err := os.Stat(s.abs); err != nil || info.Size() != s.size || !info.ModTime().Equal(s.mtime) { /* changed; re-analyze */ }
}

Try / catch

if code == 346 {
    // the named file changed; re-run the import so the analysis reflects the current content
}

Prevention

When it happens

Trigger: validateObsidianSourceMetadata(doc.Source) returns an error in the per-document loop — the .md file's size or modification time differs from the analysis snapshot, typically because the file was edited or re-synced after analysis.

Common situations: Obsidian open with autosave writing to the file during import; sync client downloading a newer version mid-import; build/backup tool touching mtimes without content changes.

Related errors


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

Appendix: source

Thrown at kernel/model/import_obsidian.go:1867

	}
	if len(vault.Assets) != len(fresh.Assets) {
		return newObsidianUserError(349, "", errors.New("the attachment file list changed after analysis"))
	}
	for key := range vault.Assets {
		if fresh.Assets[key] == nil {
			return newObsidianUserError(349, "", errors.New("the attachment file list changed after analysis"))
		}
	}
	for _, doc := range vault.Docs {
		if doc.Synthetic {
			continue
		}
		if err := ctx.Err(); err != nil {
			return err
		}
		if err := validateObsidianSourceMetadata(doc.Source); err != nil {
			return newObsidianUserError(346, doc.Source.RelPath,
				fmt.Errorf("Markdown [%s] changed after analysis: %w", doc.Source.RelPath, err))
		}
	}
	for _, asset := range vault.ImportAssets {
		if err := ctx.Err(); err != nil {
			return err
		}
		if err := validateObsidianSourceMetadata(asset.Source); err != nil {
			return newObsidianUserError(346, asset.Source.RelPath,
				fmt.Errorf("attachment [%s] changed after analysis: %w", asset.Source.RelPath, err))
		}
	}
	return nil
}

func validateObsidianSourceMetadata(file *obsidianSourceFile) error {
	info, err := os.Stat(file.AbsPath)
	if err != nil {
		return err

View on GitHub (pinned to 9f775e8a12)