{"record":{"id":"26311b8fad7ad4e3","repo":"siyuan-note/siyuan","slug":"asset-data-is-empty","errorCode":null,"errorMessage":"asset data is empty","messagePattern":"asset data is empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/upload.go","lineNumber":46,"sourceCode":"\n\t\"github.com/88250/gulu\"\n\t\"github.com/88250/lute/ast\"\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/siyuan-note/filelock\"\n\t\"github.com/siyuan-note/logging\"\n\t\"github.com/siyuan-note/siyuan/kernel/cache\"\n\t\"github.com/siyuan-note/siyuan/kernel/treenode\"\n\t\"github.com/siyuan-note/siyuan/kernel/util\"\n)\n\n// InsertAssetBytes 将内存中的资源直接写入目标文档资源目录，避免生成内容经过明文临时文件。\nfunc InsertAssetBytes(id, fileName string, data []byte) (assetPath string, created bool, err error) {\n\tbt := treenode.GetBlockTree(id)\n\tif bt == nil {\n\t\treturn \"\", false, errors.New(Conf.Language(71))\n\t}\n\tif len(data) == 0 {\n\t\treturn \"\", false, errors.New(\"asset data is empty\")\n\t}\n\n\tbaseName := filepath.Base(fileName)\n\tfName := util.FilterUploadFileName(baseName)\n\text := strings.ToLower(filepath.Ext(fName))\n\tfName = strings.TrimSuffix(fName, filepath.Ext(fName)) + ext\n\tif fName == \"\" || fName == \".\" || ext == \"\" {\n\t\treturn \"\", false, errors.New(\"invalid asset filename\")\n\t}\n\n\tdocDirLocalPath := filepath.Join(util.DataDir, bt.BoxID, path.Dir(bt.Path))\n\tassetsDirPath := getAssetsDir(filepath.Join(util.DataDir, bt.BoxID), docDirLocalPath)\n\tif err = os.MkdirAll(assetsDirPath, 0755); err != nil {\n\t\treturn \"\", false, err\n\t}\n\n\treader := bytes.NewReader(data)\n\thash, err := util.GetEtagByHandle(reader, int64(len(data)))","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/upload.go#L28-L64","documentation":"Thrown by InsertAssetBytes when the data byte slice has length zero. The function exists to write in-memory asset bytes directly to disk (bypassing temp files), so empty data is a programming error — there is nothing to write. This guard runs before any filesystem operation.","triggerScenarios":"Passing a nil or zero-length []byte to InsertAssetBytes. This happens when an upstream read returned no data (e.g., an empty file upload, a failed network read that produced an empty buffer, or a bug that initializes a byte slice but never populates it).","commonSituations":"The upstream caller read from a closed reader, an HTTP multipart upload had a zero-length body, or the asset data pipeline dropped the buffer before reaching InsertAssetBytes.","solutions":["Check len(data) > 0 before calling InsertAssetBytes","Trace the data source — verify the reader/file that populated the byte slice actually produced bytes","If the asset is legitimately zero-length, decide whether it should be skipped rather than inserted"],"exampleFix":"// before\nassetPath, created, err := model.InsertAssetBytes(id, name, data)\n\n// after\nif len(data) == 0 {\n    return errors.New(\"cannot insert empty asset\")\n}\nassetPath, created, err := model.InsertAssetBytes(id, name, data)","handlingStrategy":"validation","validationCode":"if len(data) == 0 {\n    return errors.New(\"cannot insert empty asset data\")\n}\nassetPath, created, err := model.InsertAssetBytes(id, fileName, data)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate that byte buffers are non-empty before passing to asset insertion APIs","Log the data source and size when asset data is unexpectedly empty to trace upstream read failures","For HTTP upload handlers, reject zero-length request bodies before they reach InsertAssetBytes"],"tags":["asset","upload","validation","kernel"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}