#1050·n8n-mcp

Legacy Cron node (n8n-nodes-base.cron) not recognized as activatable trigger — isTriggerNode() only matches nodes with 'trigger' in the type name

Author: plittlefieldCreated Sep 2, 2026Updated Sep 2, 2026

Description

Calling n8n_update_partial_workflow on any active workflow that uses the legacy n8n-nodes-base.cron trigger node fails validation with:

Cannot activate workflow: No activatable trigger nodes found. Workflows must have at least one enabled trigger node (webhook, schedule, executeWorkflowTrigger, etc.).

This happens on any update to the workflow (not just activate/deactivate calls) — a plain updateNode/patchNodeField edit to an unrelated node is enough to trigger it, because validation re-checks the whole workflow's trigger eligibility on every save while active: true.

The workflow is, and remains, fully active and running correctly in n8n itself the entire time — this is purely a client-side validation false negative in n8n-mcp, not a real n8n limitation. Confirmed by calling n8n's own REST API directly (POST /workflows/{id}/activate and /deactivate) for the same workflow — both succeed with HTTP 200 every time.

This is the same class of bug as #538 (IMAP Email Trigger), just for a different, more common node type.

Root Cause (confirmed by reading source)

src/utils/node-type-utils.ts:

export function isTriggerNode(nodeType: string): boolean {
  const normalized = normalizeNodeType(nodeType);
  const lowerType = normalized.toLowerCase();

  if (lowerType.includes('trigger')) {
    return true;
  }
  if (lowerType.includes('webhook') && !lowerType.includes('respond')) {
    return true;
  }
  if (lowerType.includes('emailread') || lowerType.includes('emailreadimap')) {
    return true;
  }
  return normalized === 'nodes-base.start';
}

n8n-nodes-base.cron normalizes to nodes-base.cron, which does not contain the substring trigger, webhook, or emailread, and is not nodes-base.start — so it falls through to false.

Notably, the docstring immediately above isActivatableTrigger (which calls isTriggerNode) explicitly claims it handles this case:

/**
 * Check if a node is an ACTIVATABLE trigger
 * ...
 * Returns true for:
 * - Webhook triggers (webhook, webhookTrigger)
 * - Time-based triggers (schedule, cron)   <-- claims "cron" is handled
 * ...
 */

...but the actual isTriggerNode implementation never checks for 'cron' as a substring anywhere. The docstring and the code have drifted apart.

Steps to Reproduce

  1. Have an active workflow using a legacy n8n-nodes-base.cron trigger node (as opposed to n8n-nodes-base.scheduleTrigger)
  2. Call n8n_update_partial_workflow with any operation targeting a different node in the same workflow, e.g.:
    n8n_update_partial_workflow({
      id: "<workflow-id>",
      operations: [{ type: "patchNodeField", nodeName: "SomeOtherNode", fieldPath: "parameters.someField", patches: [{ find: "a", replace: "b" }] }]
    })
    
  3. Validation fails with "No activatable trigger nodes found", operation is not saved, even though the Cron node is enabled and the workflow is genuinely active and running in n8n

Suggested Fix

Add a check for 'cron' as a substring in isTriggerNode, matching what the docstring already claims and matching how emailread is already handled:

if (lowerType.includes('cron')) {
  return true;
}

Environment

  • n8n-mcp version: 2.77.0 (confirmed via n8n_health_check, reported as up to date)
  • n8n version: 2.37.7
  • Node type: n8n-nodes-base.cron (typeVersion 1)

Workaround

Two working options, confirmed directly:

  1. Call n8n's REST API directly (POST /workflows/{id}/activate / /deactivate, or PUT /workflows/{id}), bypassing n8n-mcp's client-side validation entirely.
  2. Replace the legacy Cron node with the modern n8n-nodes-base.scheduleTrigger node — its type name contains trigger, so it passes isTriggerNode() and the whole class of error goes away permanently for that workflow.