{"record":{"id":"22345e85a394a5fd","repo":"siyuan-note/siyuan","slug":"path-is-not-a-child-of-assets-directory-s","errorCode":null,"errorMessage":"path is not a child of assets directory: %s","messagePattern":"path is not a child of assets directory: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/assets.go","lineNumber":1077,"sourceCode":"\t\t\tif !filelock.IsExist(boxConfPath) {\n\t\t\t\terr = fmt.Errorf(\"asset path does not belong to a notebook: %s\", assetPath)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif IsEncryptedBox(parts[0]) {\n\t\t\t\terr = fmt.Errorf(\"accessing assets in encrypted notebook [%s] is not supported\", parts[0])\n\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}","sourceCodeStart":1059,"sourceCodeEnd":1095,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/assets.go#L1059-L1095","documentation":"This is a defense-in-depth check in ResolveDataAssetPath: after locating the `assets` directory segment and joining it to DataDir, the function verifies with gulu.File.IsSubPath that the requested absolute path is actually contained within that assets root. Because earlier cleaning should guarantee containment, hitting this error usually means the computed absPath and the assets root are inconsistent (e.g. edge cases in path separators or the `assets` segment matched at an unexpected index).","triggerScenarios":"ResolveDataAssetPath called such that absPath (DataDir + cleaned relative path) is not a filesystem child of assetRoot — practically rare since assetRoot is a prefix of absPath; can surface on Windows with unusual path casing/separator forms.","commonSituations":"Non-normalized paths on Windows (mixed separators, differing case); custom/patched callers that mutate absPath between checks; pathological paths like the leaf being the assets directory itself (`nb/assets`).","solutions":["Verify the input path is a clean slash-relative path ending in a real file name under `assets/`, not the assets directory itself","Pass the path through filepath.ToSlash/clean before calling, e.g. `path.Clean(strings.ReplaceAll(p, \"\\\\\", \"/\"))`","If it occurs on Windows, check path casing/separator normalization in the code that produced the path","Report as a bug with the exact input path if a plainly valid `assets/...` path triggers it"],"exampleFix":"// before\nrel, abs, err := model.ResolveDataAssetPath(\"nb\\\\assets\\\\sub\\\\..\\\\pic.png\")\n// after\nrel, abs, err := model.ResolveDataAssetPath(path.Clean(\"nb/assets/sub/../pic.png\"))","handlingStrategy":"validation","validationCode":"p := path.Clean(strings.ReplaceAll(assetPath, \"\\\\\", \"/\"))\nif strings.HasSuffix(p, \"/assets\") {\n    return errors.New(\"path must point to a file inside assets/, not the assets dir itself\")\n}","typeGuard":null,"tryCatchPattern":"rel, abs, err := model.ResolveDataAssetPath(assetPath)\nif err != nil {\n    if strings.Contains(err.Error(), \"not a child of assets directory\") {\n        return fmt.Errorf(\"malformed asset path %q: %w\", assetPath, err)\n    }\n    return err\n}","preventionTips":["Pass clean, forward-slash relative paths with a concrete file leaf under assets/","Never point at the assets directory itself as the target","On Windows, avoid mixed separators and keep casing consistent with on-disk names","Treat this error as a caller bug: log the raw input path for diagnosis"],"tags":["go","filesystem","path","assets"],"backgroundTag":"invalid-argument-value","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"}