siyuan-note/siyuan · error

resolve file annotation asset

Error message

resolve file annotation asset [%s]: %w

What it means

During PDF file-annotation export, the kernel resolves the asset link referenced by an annotation to an absolute path inside a notebook via GetAssetAbsPathInBox. When that lookup fails (asset missing, link malformed, or asset outside the box), export of that file annotation is aborted with this wrapped error, preserving the original source rather than exporting incomplete data.

Solutions

  1. Verify the asset file exists in the notebook's assets folder at the path referenced by the annotation link
  2. Re-insert or re-link the PDF annotation so the asset link points to an existing asset
  3. Check the notebook (boxID) matches the notebook that actually owns the asset
  4. Rebuild the asset reference by re-uploading the PDF and recreating the annotation

Example fix

// before: annotation points to a deleted asset
![annotation](assets/old-report.pdf)
// after: re-link to the existing asset
![annotation](assets/report.pdf)
Defensive patterns

Strategy: validation

Validate before calling

const assetPath = "assets/report.pdf";
const resp = await fetchPost("/api/filetree/getDocInfo", {id: docID}); // confirm annotation link resolves
// ensure the file exists before export:
const stat = await fetchPost("/api/file/getFileStat", {path: assetPath});

Try / catch

try {
  await exportAnnotation(docID);
} catch (e) {
  if (/resolve file annotation asset/.test(e.message)) {
    console.warn("Asset missing; re-link the PDF before exporting", e.cause);
  }
}

Prevention

When it happens

Trigger: Calling the export path for a PDF whose .sya annotation asset link cannot be resolved: the asset was moved/renamed/deleted from the notebook's assets folder, the lookupLink is malformed (bad query suffix), or the asset belongs to a different box than boxID.

Common situations: Exporting a document after the referenced PDF was deleted or moved out of assets/; assets synced from another device where the file was removed; hand-edited markdown pointing to a wrong asset path; notebook ID mismatch when exporting across boxes.

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/0ac9f1081018fdbe. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/export.go:4357

	assetLink, annotationID := util.SplitFileAnnotationRef(refID)
	if "" == annotationID {
		return fmt.Errorf("invalid file annotation reference [%s]", refID)
	}
	p, query, _ := splitAssetReference(assetLink)
	assetLink = p
	if "" != query {
		assetLink += "?" + query
	}
	lookupLink := p
	if decodedPath, decodeErr := url.PathUnescape(p); nil == decodeErr {
		lookupLink = decodedPath
	}
	if "" != query {
		lookupLink += "?" + query
	}
	absPath, err := GetAssetAbsPathInBox(lookupLink, boxID)
	if err != nil {
		return fmt.Errorf("resolve file annotation asset [%s]: %w", assetLink, err)
	}
	sya := absPath + ".sya"
	// 以实际资源所属笔记本认证标注密文,读取失败时保留源文件并中止导出。
	assetBoxID := ExtractBoxIDFromAssetsPath(absPath)
	var dek []byte
	if IsEncryptedBox(assetBoxID) {
		HoldBoxReadLock(assetBoxID)
		defer ReleaseBoxReadLock(assetBoxID)
		dek, err = GetDEKIfUnlocked(assetBoxID)
		if err != nil {
			return err
		}
		defer clear(dek)
	}
	syaData, readErr := os.ReadFile(sya)
	if readErr != nil {
		return fmt.Errorf("read file annotation [%s]: %w", sya, readErr)
	}

View on GitHub (pinned to 9f775e8a12)