{"record":{"id":"5e4642b868ab84ee","repo":"siyuan-note/siyuan","slug":"asset-path-must-be-absolute","errorCode":null,"errorMessage":"asset path must be absolute","messagePattern":"asset path must be absolute","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/asset_download.go","lineNumber":117,"sourceCode":"\tif Conf.Sync == nil || !Conf.Sync.Enabled || Conf.GetUser() == nil {\n\t\treturn errors.New(Conf.Language(376))\n\t}\n\tswitch Conf.Sync.Provider {\n\tcase conf.ProviderSiYuan:\n\t\tif !IsSubscriber() {\n\t\t\treturn errors.New(Conf.Language(376))\n\t\t}\n\tcase conf.ProviderS3, conf.ProviderWebDAV, conf.ProviderLocal:\n\t\tif !IsPaidUser() {\n\t\t\treturn errors.New(Conf.Language(376))\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc dataRelativeAssetPath(absPath string) (string, error) {\n\tif !filepath.IsAbs(absPath) {\n\t\treturn \"\", fmt.Errorf(\"asset path must be absolute\")\n\t}\n\trel, err := filepath.Rel(util.DataDir, filepath.Clean(absPath))\n\tif err != nil || rel == \"..\" || strings.HasPrefix(rel, \"..\"+string(filepath.Separator)) {\n\t\treturn \"\", fmt.Errorf(\"asset path is outside data directory\")\n\t}\n\tif rel == \".\" {\n\t\treturn \"/\", nil\n\t}\n\treturn \"/\" + filepath.ToSlash(rel), nil\n}\n\n// EnsureAssetLocal 在调用方完成访问校验后补齐资源，下载内容仍由原有读取流程认证。\nfunc EnsureAssetLocal(absPath string) error {\n\tif _, err := os.Stat(absPath); err == nil {\n\t\treturn nil\n\t} else if !errors.Is(err, os.ErrNotExist) {\n\t\treturn err\n\t}","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/asset_download.go#L99-L135","documentation":"dataRelativeAssetPath validates that an asset path is absolute before converting it to a data-directory-relative slash path. SiYuan throws this because a relative path cannot be safely related to util.DataDir, which would produce a meaningless or dangerous result.","triggerScenarios":"Passing a relative path (e.g. \"assets/foo.png\") to EnsureAssetLocal or EnsureAssetPrefixLocal instead of an absolute filesystem path.","commonSituations":"Storing asset paths from database fields that hold relative paths; passing user-supplied or UI-derived paths without resolving them against the workspace; using paths returned from APIs that document relative results.","solutions":["Resolve the path to absolute before calling, e.g. filepath.Join(util.DataDir, relPath)","Check filepath.IsAbs(path) before invoking the API","Use GetAssetAbsPath or a similar API that returns absolute paths"],"exampleFix":"// before\nerr := model.EnsureAssetLocal(\"assets/foo.png\")\n// after\nabs := filepath.Join(util.DataDir, \"assets/foo.png\")\nerr := model.EnsureAssetLocal(abs)","handlingStrategy":"validation","validationCode":"if !filepath.IsAbs(p) {\n  p = filepath.Join(util.DataDir, p)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always resolve asset paths to absolute before calling EnsureAssetLocal/EnsureAssetPrefixLocal","Store absolute paths or convert early at ingestion boundaries","Add a filepath.IsAbs assertion in dev builds"],"tags":["path","validation","assets"],"backgroundTag":"invalid-argument-format","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}