toeverything/AFFiNE · error · Error
-32001
-32001
Error message
Title cannot be empty
What it means
Error('Title cannot be empty') thrown by the MCP doc-creation tool after it collapses all \r\n runs to spaces and trims: if nothing remains, the title is unusable. The MCP controller catches tool execution errors and returns them as JSON-RPC code -32001 'Error executing tool: Title cannot be empty'. It fires after the Workspace.CreateDoc permission check, so authorized callers with junk titles still hit it.
Source
Thrown at packages/backend/server/src/plugins/copilot/mcp/provider.ts:244
type: 'string',
description: 'The markdown content for the document body',
},
},
required: ['title', 'content'],
additionalProperties: false,
},
execute: async ({ title, content }, options) => {
try {
await this.ac
.user(userId)
.workspace(workspaceId)
.assert('Workspace.CreateDoc');
const abortedAfterPermission = abortIfNeeded(options.signal);
if (abortedAfterPermission) return abortedAfterPermission;
const sanitizedTitle = title.replace(/[\r\n]+/g, ' ').trim();
if (!sanitizedTitle) throw new Error('Title cannot be empty');
const strippedContent = content.replace(
/^[ \t]{0,3}#\s+[^\n]*#*\s*\n*/,
''
);
const result = await this.writer.createDoc(
workspaceId,
sanitizedTitle,
strippedContent,
userId
);
return toolText(
JSON.stringify({
success: true,
docId: result.docId,
message: `Document "${title}" created successfully`,
})
);View on GitHub (pinned to b4c8548c09)
Solutions
- Validate/trim the title client- (or agent-)side and require at least one non-whitespace character
- Fall back to a generated default title (e.g. 'Untitled' or a date-based name) when the derived title is blank
- Sanitize earlier: strip markdown '#' prefixes before sending, since the tool also strips them from content
Example fix
// before
tools.call('create_doc', { title: '\n \n', content });
// after
const title = rawTitle.replace(/[\r\n]+/g, ' ').trim() || 'Untitled';
tools.call('create_doc', { title, content }); Defensive patterns
Strategy: validation
Validate before calling
const sanitizedTitle = title.replace(/[\r\n]+/g, ' ').trim();
if (!sanitizedTitle) throw new Error('Derived title is empty — supply a default');
await tools.call('create_doc', { title: sanitizedTitle, content }); Type guard
const isNonEmptyTitle = (t: unknown): t is string => typeof t === 'string' && t.replace(/[\r\n]+/g, ' ').trim().length > 0;
Try / catch
try {
return await tools.call('create_doc', { title, content });
} catch (e) {
if (e.code === -32001 && /Title cannot be empty/.test(e.message)) {
return tools.call('create_doc', { title: 'Untitled', content });
}
throw e;
} Prevention
- Always sanitize+trim agent-derived titles and fall back to a default
- Add minLength/trim rules to the tool's inputSchema so bad titles fail as params errors
- Log the source of empty titles to find the upstream extraction bug
When it happens
Trigger: Calling the create-doc MCP tool with a title of only newlines/spaces; a title that is empty after stripping markdown heading noise; an LLM passing a whitespace-only title argument.
Common situations: Agent derives the title from a document whose first line is blank; title string built from a template with empty interpolation; prompt-injection or parsing bug yields '\n\n'.
Related errors
- can_not_batch_grant_doc_owner_permissions
- can_not_batch_grant_doc_owner_permissions
- Unsupported MCP credential expiration
- MCP credential name is required
- expect_to_publish_doc
AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18).
Data as JSON: /api/errors/d496c22f9d465e96.
Report an issue: GitHub.