siyuan-note/siyuan · error

asset data is empty

Error message

asset data is empty

What it means

InsertAssetBytes rejects an empty byte slice. An asset with zero bytes has no meaningful content (and would break hashing/dedup), so the kernel fails fast instead of writing a 0-byte file into the assets directory.

Solutions

  1. Check len(data) > 0 in the caller before calling InsertAssetBytes and treat an empty buffer as an upstream failure
  2. Fix the producer of the bytes (re-download the image, re-run the generator) — the empty payload is the real bug
  3. Verify the source file is not 0 bytes (os.Stat before reading)
  4. Log the upstream step's error instead of discarding it; an empty body usually masks an earlier failed read

Example fix

// before
assetPath, _, _ := model.InsertAssetBytes(id, name, data)
// after
if len(data) == 0 {
    return errors.New("asset source returned empty data; aborting insert")
}
assetPath, _, err := model.InsertAssetBytes(id, name, data)
Defensive patterns

Strategy: validation

Validate before calling

if data == nil || len(data) == 0 {
    return errors.New("refusing to insert empty asset payload")
}

Try / catch

assetPath, _, err := model.InsertAssetBytes(id, name, data)
if err != nil && strings.Contains(err.Error(), "asset data is empty") {
    // propagate upstream producer failure; do not retry with the same empty buffer
}

Prevention

When it happens

Trigger: Calling InsertAssetBytes with data == nil or len(data) == 0 — e.g. a read of the source image/attachment returned no bytes, a truncated download was passed through, or the caller sliced an empty buffer.

Common situations: AI/image generation returned no payload; reading a file that is 0 bytes on disk; a download error swallowed earlier and an empty body propagated to the insert step.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/26311b8fad7ad4e3. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/upload.go:47

	"github.com/88250/gulu"
	"github.com/88250/lute/ast"
	"github.com/gin-gonic/gin"
	"github.com/siyuan-note/filelock"
	"github.com/siyuan-note/logging"
	"github.com/siyuan-note/siyuan/kernel/cache"
	"github.com/siyuan-note/siyuan/kernel/treenode"
	"github.com/siyuan-note/siyuan/kernel/util"
)

// InsertAssetBytes 将内存中的资源直接写入目标文档资源目录,避免生成内容经过明文临时文件。
func InsertAssetBytes(id, fileName string, data []byte) (assetPath string, created bool, err error) {
	bt := treenode.GetBlockTree(id)
	if bt == nil {
		return "", false, errors.New(Conf.Language(71))
	}
	if len(data) == 0 {
		return "", false, errors.New("asset data is empty")
	}

	baseName := filepath.Base(fileName)
	fName := util.FilterUploadFileName(baseName)
	ext := strings.ToLower(filepath.Ext(fName))
	fName = strings.TrimSuffix(fName, filepath.Ext(fName)) + ext
	if fName == "" || fName == "." || ext == "" {
		return "", false, errors.New("invalid asset filename")
	}

	docDirLocalPath := filepath.Join(util.DataDir, bt.BoxID, path.Dir(bt.Path))
	assetsDirPath := getAssetsDir(filepath.Join(util.DataDir, bt.BoxID), docDirLocalPath)
	if err = os.MkdirAll(assetsDirPath, 0755); err != nil {
		return "", false, err
	}

	reader := bytes.NewReader(data)
	hash, err := util.GetEtagByHandle(reader, int64(len(data)))

View on GitHub (pinned to 9f775e8a12)