{"record":{"id":"c0ebcdca3a8c976b","repo":"siyuan-note/siyuan","slug":"ambiguous-asset-path-s","errorCode":null,"errorMessage":"ambiguous asset path: %s","messagePattern":"ambiguous asset path: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/asset_relink.go","lineNumber":269,"sourceCode":"\t\tif !info.Mode().IsRegular() {\n\t\t\treturn \"\", fmt.Errorf(\"asset must be a regular file: %s\", assetPath)\n\t\t}\n\t\treal, err := filepath.EvalSymlinks(candidate)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\trealRoot, err := filepath.EvalSymlinks(root)\n\t\tif err != nil || !gulu.File.IsSubPath(realRoot, real) {\n\t\t\treturn \"\", fmt.Errorf(\"asset escapes its directory: %s\", assetPath)\n\t\t}\n\t\tif err = validateRelinkStoragePath(real); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tif IsEncryptedAssetPath(real) {\n\t\t\treturn \"\", errors.New(\"encrypted assets are not supported\")\n\t\t}\n\t\tif found != \"\" && found != candidate {\n\t\t\treturn \"\", fmt.Errorf(\"ambiguous asset path: %s\", assetPath)\n\t\t}\n\t\tfound = candidate\n\t}\n\tif required && found == \"\" {\n\t\treturn \"\", fmt.Errorf(\"target asset does not exist locally: %s\", assetPath)\n\t}\n\treturn found, nil\n}\n\nfunc (p *assetRelinkPlan) scanViews() error {\n\tdir := filepath.Join(util.DataDir, \"storage\", \"av\")\n\tif err := p.observe(dir); err != nil {\n\t\treturn err\n\t}\n\tentries, err := os.ReadDir(dir)\n\tif err != nil && !os.IsNotExist(err) {\n\t\treturn err\n\t}","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/asset_relink.go#L251-L287","documentation":"During asset relink preflight, resolveRelinkAsset checks each notebook's assets/ root for the referenced asset. If the same asset-relative path resolves to an existing regular file under more than one root and the candidates differ, the path is ambiguous — the relinker cannot know which file the reference means, so it aborts rather than silently relinking to the wrong file.","triggerScenarios":"An asset reference like 'assets/foo.png' exists in two different notebook asset roots (e.g. notebook1/assets/foo.png and notebook2/assets/foo.png), or in a notebook assets dir plus a shared/synced assets root, and resolveRelinkAsset is invoked with multiple roots by scanMetadata during a relink scan.","commonSituations":"The same file was copied into two notebooks' assets folders; synced workspaces merged duplicate assets; a shared asset root was added alongside notebook-local assets so both resolve the same relative path.","solutions":["Rename the asset in one notebook so the relative path is unique per root","Delete the duplicate copy that is not actually referenced and update references to point at the surviving asset","Relink with a single asset root so the path can only resolve once","If intentional sharing is needed, keep one canonical copy under one root and reference it via that root only"],"exampleFix":"// before (two roots both contain assets/foo.png)\nresolveRelinkAsset([]string{nb1Assets, nb2Assets}, \"assets/foo.png\", true)\n// error: ambiguous asset path: assets/foo.png\n// after (rename one copy so each root has a unique path)\nresolveRelinkAsset([]string{nb1Assets, nb2Assets}, \"assets/foo.png\", true) // nb1/assets/foo.png -> nb1/assets/foo-1.png","handlingStrategy":"validation","validationCode":"// before relinking, ensure each referenced asset path resolves to exactly one root\nfunc isUnambiguous(roots []string, assetPath string) bool {\n\tcount := 0\n\tfor _, root := range roots {\n\t\tif _, err := os.Stat(filepath.Join(root, strings.TrimPrefix(assetPath, \"assets/\"))); err == nil {\n\t\t\tcount++\n\t\t}\n\t}\n\treturn count <= 1\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep asset filenames unique across notebooks","Avoid copying the same asset into multiple assets/ folders; reference one canonical copy","Audit assets/ directories for duplicate filenames before running relink"],"tags":["assets","ambiguity","relink","filesystem"],"backgroundTag":"conflicting-config-options","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}