siyuan-note/siyuan · error

read image failed: %w

Error message

read image failed: %w

What it means

Returned by PrepareDocumentImage when ReadAssetBytesInBox(bt.BoxID, assetPath) fails. ReadAssetBytesInBox resolves the asset to an absolute path inside the box and os.ReadFile's it (decrypting for encrypted notebooks). The %w wraps the underlying OS read, path-resolution, or decryption error.

Source

Thrown at kernel/model/assets.go:396

// PrepareDocumentImage 校验并读取文档实际引用的本地资源图片,供当前模型直接接收图片输入。
func PrepareDocumentImage(documentID, assetPath string) (PreparedDocumentImage, error) {
	assetPath = strings.TrimSpace(assetPath)
	if assetPath == "" {
		return PreparedDocumentImage{}, errors.New("assetPath is required for analyze")
	}
	if !strings.HasPrefix(AssetPathWithoutQuery(assetPath), "assets/") {
		return PreparedDocumentImage{}, errors.New("only local assets/... images are supported")
	}
	bt, err := resolveMultimodalDocument(documentID)
	if err != nil {
		return PreparedDocumentImage{}, err
	}
	if !documentReferencesImage(bt.RootID, assetPath) {
		return PreparedDocumentImage{}, errors.New("assetPath is not an image referenced by the document")
	}
	data, err := ReadAssetBytesInBox(bt.BoxID, assetPath)
	if err != nil {
		return PreparedDocumentImage{}, fmt.Errorf("read image failed: %w", err)
	}
	prepared, err := util.PrepareModelImage(
		data, documentImageMaxBytes, documentImageMaxPixels, documentImageMaxEdge,
	)
	if err != nil {
		return PreparedDocumentImage{}, err
	}
	return PreparedDocumentImage{
		Artifact: ImageArtifactRef{Kind: "image", Path: assetPath, DocumentID: bt.RootID},
		Data:     prepared.Data,
		MIMEType: prepared.MIMEType,
		Prepared: prepared,
	}, nil
}

// GenerateImage 使用全局图片生成配置创建图片字节,可供文档资源、编辑器和其他图片入口复用。
func GenerateImage(ctx context.Context, request GenerateImageRequest) (GenerateImageResult, error) {
	if Conf == nil || Conf.AI == nil {

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Inspect the wrapped error: os.PathError means missing/locked file, a decryption error means the box is locked — unlock it first.
  2. If the asset is gone, re-insert it into the document and update the reference before retrying.
  3. For encrypted notebooks, ensure the user has unlocked the notebook in this kernel session before calling.

Example fix

// before
prepared, err := model.PrepareDocumentImage(docID, p)
if err != nil { return err }

// after — classify the wrapped read error
prepared, err := model.PrepareDocumentImage(docID, p)
if err != nil {
    if errors.Is(err, os.ErrNotExist) {
        // asset missing on disk — refresh doc references
        return fmt.Errorf("asset vanished, re-add it: %w", err)
    }
    return err
}
Defensive patterns

Strategy: try-catch

Try / catch

prepared, err := model.PrepareDocumentImage(documentID, assetPath)
if err != nil {
    if errors.Is(err, fs.ErrNotExist) {
        // asset gone from disk — refresh references, do not retry unchanged
        return fmt.Errorf("asset missing on disk: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: The asset was deleted from disk but is still referenced by the AST (index/disk drift); the file is locked or unreadable due to permissions; an encrypted-notebook asset is requested but the box read lock / DEK cannot be acquired; the path resolves outside the box assets directory.

Common situations: Sync removed or renamed the asset; an antivirus/backup process holds an exclusive lock on Windows; the user logged out of an encrypted notebook so GetDEKIfUnlocked fails; the data dir is on a disconnected external drive.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/a2b8e119b66764a0. Report an issue: GitHub.