siyuan-note/siyuan · error

image operation failed

Error message

image operation failed

What it means

imageError builds the error CallToolResult for MCP image tools and substitutes this default message when the caller passes an empty message. It guarantees the tool result is always marked IsError with non-empty text, since an error result with blank content is useless to the MCP client.

Solutions

  1. Always pass a descriptive message (or the underlying err.Error()) into imageError instead of ""
  2. Use imageResultForError(err) for real error values so classification (e.g. IsImageExecutionUnknown) and messaging are handled
  3. Add a guard/log at call sites where the message is built dynamically to catch empty strings early

Example fix

// before
return imageError("")
// after
return imageResultForError(fmt.Errorf("render image failed: %w", err))
Defensive patterns

Strategy: try-catch

Validate before calling

if (!message || message.trim() === '') {
  message = 'image operation failed'; // or derive from err
}

Try / catch

try {
  return await imageToolCall(args);
} catch (err) {
  return imageResultForError(err); // classifies unknown-execution errors and keeps a non-empty message
}

Prevention

When it happens

Trigger: Any image tool failure path (imageHandler, imageList, runImageOperation, imageJSON, imageResultForError, imageUnknown) that reaches imageError with an empty message — typically an error value whose Error() is "" or a branch that forgets to set a message.

Common situations: Wrapping errors that stringify to empty; swallowing the underlying error into an empty variable; callers constructing errors from nil/empty fields; missing i18n or formatting that leaves the message blank.

Related errors


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

Appendix: source

Thrown at kernel/mcp/tools/image.go:346

	bt := treenode.GetBlockTree(documentID)
	if bt == nil {
		return false
	}
	_, err := model.GetAssetAbsPathInBox(assetPath, bt.BoxID)
	return err == nil
}

func imageJSON(value any) CallToolResult {
	data, err := json.Marshal(value)
	if err != nil {
		return imageError(err.Error())
	}
	return CallToolResult{Content: []ContentItem{{Type: "text", Text: string(data)}}}
}

func imageError(message string) CallToolResult {
	if message == "" {
		message = errors.New("image operation failed").Error()
	}
	return CallToolResult{Content: []ContentItem{{Type: "text", Text: message}}, IsError: true}
}

func imageResultForError(err error) CallToolResult {
	if model.IsImageExecutionUnknown(err) {
		return imageUnknown(err.Error())
	}
	return imageError(err.Error())
}

func imageUnknown(message string) CallToolResult {
	result := imageError(message)
	result.ExecutionUnknown = true
	return result
}

View on GitHub (pinned to 9f775e8a12)