siyuan-note/siyuan · error
path, size or modification time differs
Error message
path, size or modification time differs
What it means
validateObsidianSourceMetadata stats the source file on disk and compares its size and modification time with the metadata recorded during the vault analysis phase. Any difference means the file changed after analysis, so the import treats it as untrustworthy and fails; it is the sentinel message wrapped by error 346.
Source
Thrown at kernel/model/import_obsidian.go:1866
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
}
if info.Size() != file.Size || !info.ModTime().Equal(file.ModTime) {
return errors.New("path, size or modification time differs")
}
return nil
}
func transformObsidianMarkdown(vault *obsidianVaultContext, doc *obsidianDocPlan, data []byte) ([]byte, *obsidianTransformStats) {
scan := scanObsidianSource(data)
stats := &obsidianTransformStats{ReservedIDs: map[string]bool{}, FootnoteIDs: map[string]bool{}, PreservedComments: len(scan.Comments)}
for _, heading := range doc.Headings {
stats.ReservedIDs[heading.ID] = true
}
for _, id := range doc.BlockIDs {
stats.ReservedIDs[id] = true
}
type replacement struct {
start int
end int
text stringView on GitHub (pinned to 8641553a1f)
Solutions
- Ensure no process writes to the Obsidian vault between starting the import and its completion
- Re-run the import; the fresh analysis will record current size/mtime and validation will pass
- Import from a read-only or frozen copy of the vault
- Check for sync/backup tools resetting mtimes on vault files
Example fix
// before: file modified after analysis -> info.Size() != file.Size
if info.Size() != file.Size || !info.ModTime().Equal(file.ModTime) {
return errors.New("path, size or modification time differs")
}
// after (caller-side): re-run analysis so recorded metadata matches disk
vault := analyzeVault(vaultPath) // fresh Stat metadata
err := importVault(vault) // validation now passes Defensive patterns
Strategy: validation
Validate before calling
info, err := os.Stat(absPath)
if err != nil { return err }
if info.Size() != recorded.Size || !info.ModTime().Equal(recorded.ModTime) {
return fmt.Errorf("%s changed since analysis", recorded.RelPath)
} Try / catch
if err := importObsidianVault(p, nb); err != nil && strings.Contains(err.Error(), "modification time differs") {
vault = analyzeVault(p); err = importVault(vault) // re-analyze and retry
} Prevention
- Re-run analysis immediately before import so Stat metadata is fresh
- Avoid tools that rewrite or touch vault files during import
- Import from a read-only copy of the vault
When it happens
Trigger: os.Stat succeeds on file.AbsPath but info.Size() != file.Size or info.ModTime() is not exactly equal to the file.ModTime recorded at analysis time.
Common situations: Attachment saved/rewritten after import started; tools that rewrite a file (formatters, editors, sync clients) even if content is identical but size/mtime changed; filesystems with coarse or shifting timestamp precision; files replaced by a newer version during the import window.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- 350
- Obsidian Vault is unreadable
- Obsidian Vault path is not a directory
- Obsidian Vault config directory is missing
- Obsidian Vault has no readable Markdown
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/8ac0d980571ab3bc.
Report an issue: GitHub.