siyuan-note/siyuan · error
source changed during scan
Error message
source changed during scan: %s
What it means
To support undo/history, apply() re-reads each source file it scanned and compares it byte-for-byte with the snapshot taken during scanning. If the file cannot be read or its content differs, apply aborts with this error before writing any rewrite, ensuring no relink is applied over silently changed content.
Solutions
- Retry the whole relink request after the modifications settle so a fresh scan snapshot is taken
- Ensure no editor clients or sync jobs write to the workspace during the operation
- Serialize relink requests (don't run find and relink concurrently on the same paths)
- Check file permissions/locks if readErr (not content change) is the recurring cause
Example fix
// before
errCh := make(chan error, 2)
go func() { _, err := model.FindAssetReferencesBatch(ctx, paths); errCh <- err }()
go func() { _, err := model.RelinkAssets(ctx, mappings, false); errCh <- err }() // races with scan
// after
_, err := model.FindAssetReferencesBatch(ctx, paths)
_, err = model.RelinkAssets(ctx, mappings, false) // sequential; retry on 'source changed' Defensive patterns
Strategy: retry
Try / catch
_, err := model.RelinkAssets(ctx, mappings, dryRun)
if err != nil && strings.Contains(fmt.Sprint(err), "source changed during scan") {
time.Sleep(backoff)
_, err = model.RelinkAssets(ctx, mappings, dryRun) // rescan and reapply
} Prevention
- Do not edit documents while a relink runs
- Serialize find/relink operations; never run them concurrently
- Pause sync/external writers for the duration of the batch
When it happens
Trigger: A document file (.sy) covered by the plan is edited/saved between scan and apply; the file becomes unreadable (permissions, lock); external tools rewrite the file during the scan window.
Common situations: User typing in the editor while a relink batch runs; sync applying remote changes mid-scan; two concurrent relink/find operations overlapping on the same documents.
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
- workspace changed during asset scan; retry the request
- 346
- 349
- agent runtime turn changed
- agent session revision conflict
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/97d3324cae8d4f60.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/asset_relink_batch.go:457
if err = p.checkContext(); err != nil {
return err
}
historyDir, err := newAssetRelinkHistoryDir()
if err != nil {
return err
}
p.result.HistoryPath = filepath.ToSlash(historyDir)
// 所有候选源先备份;备份失败属于请求级错误,此时尚未修改任何引用。
for _, file := range files {
if err = p.checkContext(); err != nil {
return err
}
if file.before == nil {
continue
}
current, readErr := filelock.ReadFile(file.path)
if readErr != nil || !bytes.Equal(current, file.before) {
return fmt.Errorf("source changed during scan: %s", file.path)
}
rel, relErr := filepath.Rel(util.DataDir, file.path)
if relErr != nil || strings.HasPrefix(rel, "..") {
return errors.New("invalid history source path")
}
dest := filepath.Join(historyDir, rel)
if err = os.MkdirAll(filepath.Dir(dest), 0755); err != nil {
return err
}
if err = gulu.File.WriteFileSafer(dest, file.before, 0644); err != nil {
return err
}
}
indexHistoryDir(filepath.Base(historyDir), util.NewLute())
changedViews, reload := map[string]bool{}, map[string]bool{}
defer func() {
for _, tree := range p.trees {
viewChanged := falseView on GitHub (pinned to 9f775e8a12)