siyuan-note/siyuan · error

%s

Error message

%s

What it means

CopyAssetTextForRelink performs a pre-checked relink of OCR metadata from an old asset path to a new one. If the pre-check (PrepareAssetTextRelinks) records a Reason on the result — e.g. ocr_source_conflict or ocr_target_conflict — the copy is refused and that reason is surfaced as the returned error. This means relinking would overwrite or merge inconsistent OCR recognition results, so the library aborts to preserve metadata integrity.

Solutions

  1. Inspect the error string: it is exactly the plan's Reason (ocr_source_conflict or ocr_target_conflict).
  2. For ocr_source_conflict, clean up data/assets/ocr-texts.json so the old path has a single consistent OCR value, then retry.
  3. For ocr_target_conflict, pick a different NewPath or manually reconcile the OCR text for the target in ocr-texts.json before relinking.
  4. Use dryRun=true first and inspect the returned Reason before attempting a real copy.
  5. If the old asset has no OCR entry, this path returns exists=false with no error; only handle the error when Reason applies.

Example fix

// before: blind copy
exists, err := util.CopyAssetTextForRelink(oldPath, newPath, historyDir, false)
// after: dry-run pre-check and handle conflicts
if exists, err := util.CopyAssetTextForRelink(oldPath, newPath, historyDir, true); err != nil {
    log.Warnf("OCR relink refused (%s)", err) // e.g. ocr_target_conflict
} else if exists {
    util.CopyAssetTextForRelink(oldPath, newPath, historyDir, false)
}
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check with dryRun before committing
exists, err := util.CopyAssetTextForRelink(oldPath, newPath, historyDir, true)
if err != nil {
    // Reason is ocr_source_conflict or ocr_target_conflict — surface to user, do not retry blindly
    return fmt.Errorf("OCR relink pre-check failed: %w", err)
}

Try / catch

if _, err := util.CopyAssetTextForRelink(old, new, histDir, false); err != nil {
    switch {
    case strings.Contains(err.Error(), "ocr_source_conflict"):
        // clean source keys in ocr-texts.json, then retry
    case strings.Contains(err.Error(), "ocr_target_conflict"):
        // pick another NewPath or reconcile target text
    default:
        return err
    }
}

Prevention

When it happens

Trigger: Calling CopyAssetTextForRelink(oldPath, newPath, historyDir, dryRun) when (a) the old path's OCR text conflicts with other entries keyed to the same canonical path (ocr_source_conflict), or (b) the new path already exists in the index with a different OCR value or is itself conflicted / is the merge target of sources with differing values (ocr_target_conflict).

Common situations: Renaming assets after an OCR re-run left stale keys under both the raw and canonical forms of a path; renaming an asset to a name that previously held different OCR text (e.g. after restoring a backup); batch renames where multiple sources funnel into one target with divergent recognition results.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


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

Appendix: source

Thrown at kernel/util/ocr_relink.go:19

// SiYuan - From thought to insight, with agents
// Copyright (c) 2020-present, b3log.org
// SPDX-License-Identifier: AGPL-3.0-or-later

package util

import (
	"errors"
)

// CopyAssetTextForRelink 复制 OCR 元数据,保留源资源的识别结果,并拒绝覆盖不同的目标结果。
func CopyAssetTextForRelink(oldPath, newPath, historyDir string, dryRun bool) (exists bool, err error) {
	plan, err := PrepareAssetTextRelinks([]AssetTextRelinkMapping{{OldPath: oldPath, NewPath: newPath}})
	if err != nil {
		return false, err
	}
	result := plan.Results[0]
	if result.Reason != "" {
		return result.Exists, errors.New(result.Reason)
	}
	if !dryRun {
		_, err = plan.Save(historyDir, []int{0})
	}
	return result.Exists, err
}

View on GitHub (pinned to 9f775e8a12)