n8n-io/n8n · error · NodeOperationError
${error.message}
Error message
${error.message} What it means
Top-level try/catch wrapping agent.run(turnContext) inside the Microsoft 365 Agent execution handler. Any error raised during the agent loop — LLM call, tool/MCP invocation, activity callback — is re-thrown as a NodeOperationError so n8n's execution engine surfaces it with node context. The finally block separately guards observability.shutdown() with its own 5s timeout, so this error specifically represents a failure inside the agent run itself, not the observability teardown.
Source
Thrown at packages/@n8n/nodes-langchain/nodes/vendors/Microsoft/microsoft-utils.ts:702
activityCapture.output.push(activityOrText.text);
}
return await originalSendActivity(activityOrText);
};
turnContext.sendActivity = sendActivityWrapper;
const onActivity = configureActivityCallback(
nodeContext,
credentials,
mcpTokenRef,
agent.authorization,
activityCapture,
);
agent.onActivity(ActivityTypes.Message, onActivity, ['agentic']);
await agent.run(turnContext);
} catch (error) {
throw new NodeOperationError(nodeContext.getNode(), error);
} finally {
if (observability) {
try {
const OBSERVABILITY_SHUTDOWN_TIMEOUT_MS = 5000;
await Promise.race([
observability.shutdown(),
new Promise<never>((_, reject) =>
setTimeout(
() => reject(new Error('Observability shutdown timed out')),
OBSERVABILITY_SHUTDOWN_TIMEOUT_MS,
),
),
]);
} catch (error) {
// Backend unreachable or export timed out — not a code error
console.warn('Failed to shut down observability:', error);
}
}View on GitHub (pinned to 5ac6606e81)
Solutions
- Read error.message (and error.cause if present) on the thrown NodeOperationError to find the underlying agent.run failure.
- Check connectivity and credentials for every MCP server attached to the agent and for the LLM endpoint.
- Verify the authorization/turnContext are still valid for the full duration of the turn (no mid-turn expiry).
- Reproduce with observability enabled to capture the trace up to the failure point.
- If the error is transient (rate limit, network), retry the turn after backoff.
Defensive patterns
Strategy: try-catch
Validate before calling
// Validate agent prerequisites before running the turn.
function assertAgentReady(params: {
agent: { run: (t: unknown) => Promise<unknown> };
turnContext: TurnContext;
authorization: Authorization;
mcpServers: MCPServerConfig[];
}): string | null {
const { agent, turnContext, mcpServers } = params;
if (typeof agent.run !== 'function') return 'Agent is missing a run() function';
if (!turnContext?.activity) return 'turnContext.activity is missing — nothing to run on';
if (mcpServers.length === 0) return 'No MCP servers attached; agent has no tools';
return null;
}
const problem = assertAgentReady({ agent, turnContext, authorization, mcpServers });
if (problem) throw new NodeOperationError(nodeContext.getNode(), problem); Type guard
function isRunnableAgent(a: unknown): a is { run: (t: TurnContext) => Promise<unknown> } {
return !!a && typeof a === 'object' && typeof (a as { run?: unknown }).run === 'function';
} Try / catch
try {
await agent.run(turnContext);
} catch (error) {
// Preserve the original cause so the UI shows both the wrapper and the root.
throw new NodeOperationError(
nodeContext.getNode(),
error instanceof Error ? error.message : String(error),
{ cause: error },
);
} Prevention
- Always attach a cause when wrapping so the underlying agent.run failure is diagnosable.
- Validate the LLM endpoint and every MCP server is reachable before starting the turn.
- Keep observability enabled in non-production so the trace up to the failure is captured.
- Refresh authorization immediately before long turns to avoid mid-run token expiry.
When it happens
Trigger: agent.run throws because: the LLM endpoint rejected the request (auth, quota, model unavailable); an MCP tool call returned an error the agent could not recover from; configureActivityCallback's onActivity handler threw inside the message pipeline; the turnContext activity payload was malformed; or the Bot Framework agent hit an internal assertion.
Common situations: Azure OpenAI / M365 LLM endpoint rate limiting or outage; MCP server unreachable mid-turn; malformed tool response from a community MCP server; token expired mid-conversation; agentic scope authorization revoked between turn start and tool call.
Related errors
- Failed to parse agent steps
- ${toolName} was registered without a runSubAgent callback, a
- Invalid memory configuration. Use: new Memory() for in-proce
- toolCallConcurrency must be a positive integer or Infinity
- Agent "${this.name}" requires a model
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/51791e5a11d8d528.
Report an issue: GitHub.