eyaltoledano/claude-task-master · error · Error

Invalid message format. Each message must have role and cont

Error message

Invalid message format. Each message must have role and content

What it means

This error is thrown by BaseAIProvider.validateMessages when a message in the messages array is missing either a 'role' or a 'content' property. The base provider requires every message to be a well-formed {role, content} object before it is forwarded to the underlying AI SDK. It guards all four entry points: generateText, streamText, generateObject and streamObject.

Source

Thrown at src/ai-providers/base-provider.js:242

		if (params.maxTokens !== undefined) {
			const maxTokens = Number(params.maxTokens);
			if (!Number.isFinite(maxTokens) || maxTokens <= 0) {
				throw new Error('maxTokens must be a finite number greater than 0');
			}
		}
	}

	/**
	 * Validates message array structure
	 */
	validateMessages(messages) {
		if (!messages || !Array.isArray(messages) || messages.length === 0) {
			throw new Error('Invalid or empty messages array provided');
		}

		for (const msg of messages) {
			if (!msg.role || !msg.content) {
				throw new Error(
					'Invalid message format. Each message must have role and content'
				);
			}
		}
	}

	/**
	 * Common error handler
	 */
	handleError(operation, error) {
		const errorMessage = error.message || 'Unknown error occurred';
		log('error', `${this.name} ${operation} failed: ${errorMessage}`, {
			error
		});
		throw new Error(
			`${this.name} API error during ${operation}: ${errorMessage}`
		);
	}

View on GitHub (pinned to c0c98d367c)

Solutions

  1. Ensure every message object has both a truthy 'role' ('system'|'user'|'assistant') and truthy 'content' string
  2. Filter out incomplete messages before calling: messages.filter(m => m.role && m.content)
  3. Fix code that constructs messages so empty content yields either omission of the message or placeholder content

Example fix

// before
const messages = [{ role: 'user', content: userText }, { role: 'assistant', content: '' }];
await provider.generateText({ messages });
// after
const messages = [{ role: 'user', content: userText }, { role: 'assistant', content: '' }]
  .filter(m => m.role && m.content);
await provider.generateText({ messages });
Defensive patterns

Strategy: validation

Validate before calling

function validateMessagesBeforeCall(messages) {
  if (!Array.isArray(messages) || messages.length === 0) throw new Error('messages must be a non-empty array');
  messages.forEach((m, i) => {
    if (!m || typeof m !== 'object' || !m.role || !m.content) throw new Error(`messages[${i}] must have role and content`);
  });
}

Type guard

function isValidMessage(m) {
  return typeof m === 'object' && m !== null && typeof m.role === 'string' && m.role.length > 0 && typeof m.content === 'string' && m.content.length > 0;
}

Try / catch

try {
  await provider.generateText({ messages });
} catch (e) {
  if (e.message === 'Invalid message format. Each message must have role and content') {
    console.error('Malformed message array:', messages.filter(m => !m?.role || !m?.content));
  } else throw e;
}

Prevention

When it happens

Trigger: Calling generateText/streamText/generateObject/streamObject with a messages array where at least one element lacks 'role' or 'content' (or has them set to empty string/null/undefined). E.g. messages: [{ role: 'user' }] or messages: [{ content: 'hi' }].

Common situations: Building message arrays programmatically and pushing partial objects; older message formats from a different library that use e.g. {author, text}; conditionally-built prompts where an assistant message got an empty content string after filtering; forgetting to include content on system messages.

Related errors


AI-assisted analysis of eyaltoledano/claude-task-master@c0c98d367c (2026-08-29). Data as JSON: /api/errors/2dfc98bda3504e96. Report an issue: GitHub.