n8n-io/n8n · error · Error

No message provided

Error message

No message provided

What it means

`extractMessageContent` pulls text out of a LangChain `BaseMessage`; it rejects `undefined` immediately because every downstream operation (`message.content`, `.kwargs.content`) dereferences the message. Throwing early with a precise message avoids an opaque 'cannot read properties of undefined' from the LangSmith message plumbing.

Source

Thrown at packages/@n8n/ai-workflow-builder.ee/evaluations/langsmith/types.ts:59

// Helper to format violations for display
export function formatViolations(violations: Array<{ type: string; description: string }>): string {
	if (violations.length === 0) {
		return 'All checks passed';
	}
	return `Found ${violations.length} violation(s): ${violations
		.map((v) => `${v.type} - ${v.description}`)
		.join('; ')}`;
}

// Generate a unique run ID
export function generateRunId(): string {
	return `eval-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
}

// Validate and extract message content
export function extractMessageContent(message: BaseMessage | undefined): string {
	if (!message) {
		throw new Error('No message provided');
	}

	// @ts-expect-error We need to extract content from kwargs as that's how Langsmith messages are structured
	// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
	const content = message.content ?? message.kwargs?.content;

	if (typeof content === 'string') {
		return cleanContextTags(content);
	}

	if (Array.isArray(content)) {
		// Extract text from complex content
		const textContent = content
			.filter((item) => item?.type === 'text')
			.map((item) => (item as { text: string }).text)
			.map(cleanContextTags)
			.join('\n');

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Bounds- and presence-check before calling: `if (!messages?.[0]) throw ...; extractMessageContent(messages[0])`.
  2. Filter sparse entries out of the array first: `messages.filter(Boolean)`.
  3. At the dataset source, ensure every example has at least one well-formed message.

Example fix

// before
const text = extractMessageContent(inputs.messages[0]); // inputs.messages[0] is undefined
// after
const first = inputs.messages?.find(Boolean);
if (!first) throw new Error('dataset example has no message');
const text = extractMessageContent(first);
Defensive patterns

Strategy: type-guard

Validate before calling

function firstMessage(messages: unknown[] | undefined): BaseMessage | undefined {
  if (!Array.isArray(messages) || messages.length === 0) return undefined;
  return messages.find((m) => m != null) as BaseMessage | undefined;
}
const m = firstMessage(inputs.messages);
if (!m) throw new Error('dataset example has no message to extract');
const text = extractMessageContent(m);

Type guard

function isPresentMessage(v: unknown): v is BaseMessage {
  return v != null && typeof (v as { content?: unknown }).content !== 'undefined';
}

Prevention

When it happens

Trigger: Calling `extractMessageContent(undefined)` directly, or indirectly via `extractPrompt` when `inputs.messages` exists but `messages[0]` is undefined (sparse array), or via history extraction where a turn is missing.

Common situations: Dataset example whose `messages` array is `[, something]` (hole), or a code path that calls `extractMessageContent(arr[index])` without bounds-checking; refactor that swapped a single message for an array and forgot the index.

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/0df3ff3d35128e4f. Report an issue: GitHub.