siyuan-note/siyuan · warning
OCR metadata changed during asset relink
Error message
OCR metadata changed during asset relink
What it means
Save captures a snapshot of the in-memory OCR metadata (assetsTexts) when the plan was prepared, then re-acquires the lock before writing. If the in-memory map changed between PrepareAssetTextRelinks and Save (maps.Equal fails), the plan is stale and Save aborts with "OCR metadata changed during asset relink" to avoid clobbering concurrent updates with data computed from an outdated snapshot.
Solutions
- Discard the stale plan and re-run PrepareAssetTextRelinks, then call Save promptly on the fresh plan.
- Retry the whole prepare→save sequence with a small backoff; it succeeds once concurrent OCR writes settle.
- Avoid invoking asset relink while OCR recognition is running (wait for indexing to finish).
- Serialize relink operations through a single caller so two relinks cannot race.
Example fix
// before: hold a plan for a long time then save
plan, _ := util.PrepareAssetTextRelinks(mappings)
// ... long UI wait, OCR runs meanwhile ...
plan.Save(historyDir, selected) // stale snapshot
// after: re-prepare right before saving
plan, err := util.PrepareAssetTextRelinks(mappings)
if err == nil {
plan.Save(historyDir, selected)
} Defensive patterns
Strategy: retry
Try / catch
for attempt := 0; attempt < 3; attempt++ {
plan, err := util.PrepareAssetTextRelinks(mappings)
if err != nil {
return err
}
if _, err := plan.Save(historyDir, selected); err == nil || err.Error() != "OCR metadata changed during asset relink" {
return err
}
time.Sleep(time.Duration(attempt+1) * 100 * time.Millisecond)
} Prevention
- Minimize the window between PrepareAssetTextRelinks and Save
- Wait for background OCR indexing to finish before relinking assets
- Serialize relink/save operations so concurrent relinks cannot race
- Treat this error as retryable: it is a stale-snapshot guard, not data corruption
When it happens
Trigger: Another goroutine modified the in-memory OCR metadata (e.g. via OCR recognition of new images, asset deletion cleanup, or a concurrent relink Save that replaced assetsTexts) between calling PrepareAssetTextRelinks and calling plan.Save on the same plan.
Common situations: Running asset rename/relink while background OCR indexing is still processing images; two concurrent relink operations; an OCR re-scan triggered by the user while a rename dialog's save was in flight.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- attribute view [ ] changed while loading search info
- 239
- agent runtime turn changed
- agent session revision conflict
- attribute view drop target changed; retry the drag
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/d79e047a064892c4.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/util/ocr_relink_batch.go:144
return nil, errors.New("invalid OCR mapping index")
}
result := p.Results[i]
if result.Reason != "" {
return nil, errors.New(result.Reason)
}
if !result.Changed {
continue
}
texts[(&url.URL{Path: p.mappings[i].NewPath}).EscapedPath()] = p.values[i]
changed = append(changed, i)
}
if len(changed) == 0 {
return nil, nil
}
assetsTextsLock.Lock()
defer assetsTextsLock.Unlock()
if !maps.Equal(p.memory, assetsTexts) {
return nil, errors.New("OCR metadata changed during asset relink")
}
backup, err := json.MarshalIndent(p.texts, "", " ")
if err != nil {
return nil, err
}
history := filepath.Join(historyDir, "assets", "ocr-texts.json")
if err = os.MkdirAll(filepath.Dir(history), 0755); err != nil {
return nil, err
}
if err = gulu.File.WriteFileSafer(history, backup, 0644); err != nil {
return nil, err
}
data, err := json.MarshalIndent(texts, "", " ")
if err != nil {
return nil, err
}
if err = WriteFileIfUnchanged(p.path, p.original, data); err != nil {
return nil, errView on GitHub (pinned to 9f775e8a12)