siyuan-note/siyuan · error

target asset does not exist locally

Error message

target asset does not exist locally: %s

What it means

resolveRelinkAsset requires that a referenced asset exists locally when the required flag is set. If none of the asset roots contain a regular file matching the reference path, the relink scan fails because there is no local file to relink to or to verify.

Solutions

  1. Place the missing asset file at the referenced path under one of the asset roots
  2. Re-sync the workspace so the referenced asset is downloaded to this device
  3. Fix the reference path in the document if it points to a wrong/renamed asset
  4. Remove the stale reference if the asset is no longer needed

Example fix

// before
// document references assets/old-name.png, file missing locally
// after
// copy the file into place:
os.Rename("downloads/old-name.png", filepath.Join(nbAssets, "old-name.png"))
Defensive patterns

Strategy: validation

Validate before calling

// verify the asset exists locally before triggering a relink
func assetExists(roots []string, assetPath string) bool {
	for _, root := range roots {
		if _, err := os.Stat(filepath.Join(root, strings.TrimPrefix(assetPath, "assets/"))); err == nil {
			return true
		}
	}
	return false
}

Try / catch

// wrap the relink scan to surface missing-asset errors
if err := plan.Scan(); err != nil {
	if strings.Contains(err.Error(), "target asset does not exist locally") {
		// fetch/sync the asset, then retry
	}
	return err
}

Prevention

When it happens

Trigger: A document references 'assets/missing.png' but no notebook asset root contains assets/missing.png; scanMetadata calls resolveRelinkAsset with required=true during relink preflight.

Common situations: Assets were deleted or not synced to this device; the workspace was restored from a partial backup; on-demand asset download has not fetched the file yet; the reference was hand-edited to a wrong path.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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

Appendix: source

Thrown at kernel/model/asset_relink.go:274

			return "", err
		}
		realRoot, err := filepath.EvalSymlinks(root)
		if err != nil || !gulu.File.IsSubPath(realRoot, real) {
			return "", fmt.Errorf("asset escapes its directory: %s", assetPath)
		}
		if err = validateRelinkStoragePath(real); err != nil {
			return "", err
		}
		if IsEncryptedAssetPath(real) {
			return "", errors.New("encrypted assets are not supported")
		}
		if found != "" && found != candidate {
			return "", fmt.Errorf("ambiguous asset path: %s", assetPath)
		}
		found = candidate
	}
	if required && found == "" {
		return "", fmt.Errorf("target asset does not exist locally: %s", assetPath)
	}
	return found, nil
}

func (p *assetRelinkPlan) scanViews() error {
	dir := filepath.Join(util.DataDir, "storage", "av")
	if err := p.observe(dir); err != nil {
		return err
	}
	entries, err := os.ReadDir(dir)
	if err != nil && !os.IsNotExist(err) {
		return err
	}
	for _, entry := range entries {
		if err := p.checkContext(); err != nil {
			return err
		}
		id := strings.TrimSuffix(entry.Name(), ".json")

View on GitHub (pinned to 9f775e8a12)