{"record":{"id":"12b3de77773b2c67","repo":"siyuan-note/siyuan","slug":"resolve-assets-directory-s-failed-w","errorCode":null,"errorMessage":"resolve assets directory [%s] failed: %w","messagePattern":"resolve assets directory \\[(.+?)\\] failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/assets.go","lineNumber":1083,"sourceCode":"\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\tif assetDirIndex < 0 {\n\t\terr = fmt.Errorf(\"path is not under an assets directory: %s\", assetPath)\n\t\treturn\n\t}\n\n\tassetRootParts := parts[:assetDirIndex+1]\n\tassetRoot := filepath.Join(util.DataDir, filepath.FromSlash(strings.Join(assetRootParts, \"/\")))\n\tif !gulu.File.IsSubPath(assetRoot, absPath) {\n\t\terr = fmt.Errorf(\"path is not a child of assets directory: %s\", assetPath)\n\t\treturn\n\t}\n\n\tresolvedRoot, evalErr := ResolveAssetPathWithMissingLeaf(assetRoot)\n\tif evalErr != nil {\n\t\terr = fmt.Errorf(\"resolve assets directory [%s] failed: %w\", assetRoot, evalErr)\n\t\treturn\n\t}\n\tif assetDirIndex > 0 {\n\t\tnotebookRoot := filepath.Join(util.DataDir, parts[0])\n\t\tresolvedDataDir, dataEvalErr := ResolveRealPath(util.DataDir)\n\t\tresolvedNotebookRoot, notebookEvalErr := ResolveRealPath(notebookRoot)\n\t\tif dataEvalErr != nil || notebookEvalErr != nil ||\n\t\t\t!gulu.File.IsSubPath(resolvedDataDir, resolvedNotebookRoot) ||\n\t\t\t!gulu.File.IsSubPath(resolvedNotebookRoot, resolvedRoot) {\n\t\t\terr = fmt.Errorf(\"notebook asset path resolves outside notebook directory: %s\", assetPath)\n\t\t\treturn\n\t\t}\n\t}\n\tresolvedPath, evalErr := ResolveAssetPathWithMissingLeaf(absPath)\n\tif evalErr != nil {\n\t\terr = fmt.Errorf(\"resolve asset [%s] failed: %w\", absPath, evalErr)\n\t\treturn\n\t}","sourceCodeStart":1065,"sourceCodeEnd":1101,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/assets.go#L1065-L1101","documentation":"After locating the assets root inside a notebook, ResolveDataAssetPath resolves it with ResolveAssetPathWithMissingLeaf (which evaluates symlinks while tolerating a missing final component). If that resolution fails — typically because the assets directory does not exist, is a broken symlink, or the OS rejects the path — the underlying error is wrapped with this message naming the absolute assets root.","triggerScenarios":"Calling ResolveDataAssetPath for `<notebookID>/assets/<file>` (or global `assets/...`) when the assets directory itself is missing or is an unresolvable/broken symlink, so ResolveAssetPathWithMissingLeaf returns an eval error.","commonSituations":"Assets folder deleted or renamed on disk while references remain; workspace synced/copied without the assets directory; broken symlinks after moving the workspace; permission problems on the assets directory; sync race where the notebook dir exists but its assets dir was not yet materialized.","solutions":["Create the missing assets directory (e.g. `<notebookID>/assets/`) referenced by the path","Check the error wrapped by `%w` to see whether it is not-exist vs permission vs symlink; fix accordingly","Remove or repair broken symlinks in the notebook directory","Re-sync the workspace so the assets directory is restored"],"exampleFix":"// before\nrel, abs, err := model.ResolveDataAssetPath(\"20240101120000-abc123/assets/pic.png\") // assets dir deleted\n// after\nif _, statErr := os.Stat(filepath.Join(util.DataDir, \"20240101120000-abc123\", \"assets\")); os.IsNotExist(statErr) {\n    os.MkdirAll(filepath.Join(util.DataDir, \"20240101120000-abc123\", \"assets\"), 0755)\n}\nrel, abs, err := model.ResolveDataAssetPath(\"20240101120000-abc123/assets/pic.png\")","handlingStrategy":"validation","validationCode":"func assetsDirExists(dataDir, notebookID string) bool {\n    fi, err := os.Stat(filepath.Join(dataDir, notebookID, \"assets\"))\n    return err == nil && fi.IsDir()\n}","typeGuard":null,"tryCatchPattern":"rel, abs, err := model.ResolveDataAssetPath(assetPath)\nif err != nil {\n    var pathErr *os.PathError\n    if strings.Contains(err.Error(), \"resolve assets directory\") && errors.As(err, &pathErr) && os.IsNotExist(pathErr) {\n        // recreate the assets dir and retry once\n        os.MkdirAll(filepath.Join(util.DataDir, notebookID, \"assets\"), 0755)\n        rel, abs, err = model.ResolveDataAssetPath(assetPath)\n    }\n    if err != nil {\n        return err\n    }\n}","preventionTips":["Check that the notebook's assets directory exists before resolving paths into it","Repair or remove broken symlinks after moving/copying a workspace","Ensure the sync process materializes the assets directory before references are used","Watch file/directory permissions on the workspace data dir"],"tags":["go","filesystem","symlink","assets"],"backgroundTag":"file-not-found","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"}