{"record":{"id":"6d260a99f6226226","repo":"siyuan-note/siyuan","slug":"empty-heif-image","errorCode":null,"errorMessage":"empty HEIF image","messagePattern":"empty HEIF image","errorType":"validation","errorClass":null,"httpStatus":422,"severity":"error","filePath":"kernel/heif/cache.go","lineNumber":95,"sourceCode":"\nfunc IsPath(path string) bool {\n\tpath = strings.ToLower(path)\n\tif index := strings.IndexAny(path, \"?#\"); index >= 0 {\n\t\tpath = path[:index]\n\t}\n\text := filepath.Ext(path)\n\treturn ext == \".heic\" || ext == \".heif\"\n}\n\nfunc GetOrCreate(ctx context.Context, source []byte, options Options) (Result, error) {\n\tif options.Mode != ModePreview && options.Mode != ModeThumbnail {\n\t\treturn Result{}, ErrInvalidMode\n\t}\n\tif options.Encrypted && options.BoxID == \"\" {\n\t\treturn Result{}, errors.New(\"encrypted HEIF cache requires a notebook ID\")\n\t}\n\tif len(source) == 0 {\n\t\treturn Result{}, errors.New(\"empty HEIF image\")\n\t}\n\tif len(source) > MaxInputBytes {\n\t\treturn Result{}, ErrInputTooLarge\n\t}\n\tif err := ctx.Err(); err != nil {\n\t\treturn Result{}, err\n\t}\n\n\tdigest := cacheDigest(source, options.Mode)\n\tetag := `\"heif-` + digest + `\"`\n\tcacheKey := digest\n\tif options.Encrypted {\n\t\tcacheKey = options.BoxID + \":\" + digest\n\t\tif data := getMemoryCache(cacheKey); data != nil {\n\t\t\treturn Result{Data: data, ETag: etag}, nil\n\t\t}\n\t} else if cachePath := existingDiskCache(options.Mode, digest); cachePath != \"\" {\n\t\treturn Result{Path: cachePath, ETag: etag}, nil","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/afa823b6b4e4f183511e0bc0a3be93caa94c7c97/kernel/heif/cache.go#L77-L113","documentation":"Returned by heif.GetOrCreate (kernel/heif/cache.go:95), and by convert/ImageSize, when the source slice for a .heic/.heif asset is zero bytes. GetOrCreate is called from the asset preview endpoints (kernel/server/serve.go:1255) on bytes read via ReadFileLimited, so in practice a zero-length HEIF file was stored in assets/ or passed in by the caller.","triggerScenarios":"Inserting/uploading an empty or truncated .heic file; an asset write that failed silently leaving a 0-byte file; calling the heif package directly with an empty slice (len(source) == 0).","commonSituations":"Failed phone camera exports; interrupted downloads of HEIF photos; cloud-storage placeholder files synced as 0 bytes; unit tests with empty byte-slice fixtures.","solutions":["Check the file size on disk - a 0-byte .heic must be re-exported or re-downloaded from the original source","Re-import the photo from the camera or source device","If it came from your own pipeline, fix the upload/write step that truncated it"],"exampleFix":"// before\nresult, err := heif.GetOrCreate(ctx, source, opts)  // source == []byte{}\n\n// after\nif len(source) == 0 {\n    return errors.New(\"empty HEIF image\")\n}\nresult, err := heif.GetOrCreate(ctx, source, opts)","handlingStrategy":"validation","validationCode":"// Go: guard before calling the heif package\nif len(source) == 0 {\n    return errors.New(\"empty HEIF image\")\n}\nresult, err := heif.GetOrCreate(ctx, source, opts)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate non-zero file size at upload/import time","Treat 0-byte assets as transfer failures and re-fetch the original","Stat asset files before generating previews in batch tooling"],"tags":["heif","image","validation","assets"],"backgroundTag":"empty-input-file","analyzedSha":"afa823b6b4e4f183511e0bc0a3be93caa94c7c97","analyzedAt":"2026-08-18T17:04:10.865Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}