tldraw/tldraw · error · Error

clusterIds is required: a cluster id, or an array of them

Error message

clusterIds is required: a cluster id, or an array of them

What it means

Thrown by parseClusterIds when value is not a string and not a non-empty array. parseClusterScreenshotInput calls it to validate the clusterIds argument for the take-cluster-screenshots MCP tool. A single string is accepted (wrapped into [value]) for convenience; anything else — undefined, null, an empty array, an object, a number — is rejected.

Source

Thrown at apps/dotcom/sync-worker/src/routes/tla/sharedBoardScreenshotMcp.ts:347

	page: PageSelector
	clusterIds: string[]
	theme: 'light' | 'dark'
} {
	const value = requireArgumentsObject(input)
	return {
		boardId: parseBoardId(value.boardId),
		page: parsePageSelector(value.page),
		clusterIds: parseClusterIds(value.clusterIds),
		theme: parseTheme(value.theme),
	}
}

// Accepts one id or several. A single string is allowed because asking for one cluster is the common
// case and making callers wrap it in an array is friction for nothing.
export function parseClusterIds(value: unknown): string[] {
	if (typeof value === 'string') return [parseClusterId(value)]
	if (!Array.isArray(value) || value.length === 0) {
		throw new Error('clusterIds is required: a cluster id, or an array of them')
	}
	return value.map((id) => parseClusterId(id))
}

export function parseClusterId(value: unknown): string {
	if (typeof value !== 'string' || value.length === 0) {
		throw new Error('clusterId is required')
	}
	return value
}

function requireArgumentsObject(input: unknown): Record<string, unknown> {
	if (!input || typeof input !== 'object') {
		throw new Error('Tool arguments must be an object')
	}
	return input as Record<string, unknown>
}

View on GitHub (pinned to b31086b447)

Solutions

  1. Provide clusterIds as a single id string or an array of id strings: {"clusterIds":"abc"} or {"clusterIds":["abc","def"]}.
  2. If you have a comma-separated list, split it into an array before passing.
  3. Ensure every element is a non-empty string.

Example fix

// before
{ "clusterIds": "a,b,c" }   // treated as one id, parseClusterId may reject
// after
{ "clusterIds": ["a","b","c"] }
Defensive patterns

Strategy: validation

Validate before calling

function asClusterIds(value: unknown): string[] {
  if (typeof value === 'string') return [value]
  if (Array.isArray(value) && value.length > 0) return value.filter((v): v is string => typeof v === 'string' && v.length > 0)
  throw new Error('clusterIds is required: a cluster id, or an array of them')
}

Type guard

function isClusterIdsInput(value: unknown): value is string | string[] {
  return typeof value === 'string' || (Array.isArray(value) && value.every((v) => typeof v === 'string' && v.length > 0) && value.length > 0)
}

Prevention

When it happens

Trigger: MCP tool call to take_cluster_screenshots (or parseClusterScreenshotInput directly) with clusterIds omitted, set to null, an empty array [], or a non-array non-string value. Each element must itself pass parseClusterId.

Common situations: LLM/caller omits clusterIds assuming a default; passes an array of objects instead of strings; passes a comma-separated string (which would be treated as one id and fail parseClusterId).

Related errors


AI-assisted analysis of tldraw/tldraw@b31086b447 (2026-08-12). Data as JSON: /api/errors/79e6864c6b41a8a8. Report an issue: GitHub.