Custom Trigger Node activate() event Not getting invoked

Issue

I’m building a custom trigger node that includes an activate() method intended to register a webhook with a third-party service when a workflow is activated. I’ve set activationMode: 'always' in the webhook definition, expecting the activate() method to be called automatically.

However, despite the workflow being activated in the UI and everything else working, the activate() method is never triggered — no logs, no errors, and no API call is made.

My goal is to ensure that the activate() method is called during workflow activation so I can programmatically register webhooks with an external API

Error

There is no error message. The method simply doesn’t seem to get invoked. The console.log and LoggerProxy.info in activate() never appear in logs.

Configrations/Settings

This is a custom node; the workflow is just a basic one-node setup that uses the trigger node. But here’s the relevant webhooks config:

webhooks: [{
name: ‘default’,
httpMethod: ‘POST’,
responseMode: ‘onReceived’,
path: ‘debug-webhook’,
isFullPath: false,
}]
tried with all the below flags as well and activating/deactivating the workflow
activationMode: ‘always’,
restartWebhook: true,
isActive: true,

And here’s my activate() method:
async activate(this: IHookFunctions): Promise {
console.log(‘activate called’);
LoggerProxy.info(‘activate called’);
const url = this.getNodeWebhookUrl(‘default’);
console.log(‘Webhook URL:’, url);
// Simulated registration to 3rd party
return true;
}

-n8n version:1.89.2
-Database: Postgres
-n8n EXECUTIONS_PROCESS setting default:
-Running n8n via Docker on local system
-Windows 11
— below is complete node code.

</>

 import {
  INodeType,
  INodeTypeDescription,
  IWebhookFunctions,
  IWebhookResponseData,
  ILoadOptionsFunctions,
  INodePropertyOptions,
  IHookFunctions,
  IDataObject,
  NodeApiError,
  LoggerProxy,
 } from 'n8n-workflow';
 
import { MyCustomNodeClient } from './Utils/Client';

// Single log when module is loaded
LoggerProxy.info('MyCustomNode Webhook: Module loaded');

export class MyCustomNodeWebhook implements INodeType {

constructor() {
    LoggerProxy.info('MyCustomNode Webhook: Constructor called');
}

description: INodeTypeDescription = {
    displayName: 'MyCustomNode Trigger',
    name: 'MyCustomNodeTrigger',
    icon: 'file:betterlogo.svg',
    group: ['trigger'],
    version: 1,
    description: 'Handle MyCustomNode webhook events',
    defaults: {
        name: 'MyCustomNode Trigger',
    },
    inputs: [],
    outputs: ['main'],
    credentials: [
        {
            name: 'myCustomNodeApi',
            required: true,
        },
    ],
    webhooks: [
        {
            name: 'default',
            httpMethod: 'POST',
            responseMode: 'onReceived',
            path: 'webhook',
            // isActive: true,
            // restartWebhook: true,
            // activationMode: 'always',
        },
    ],
    properties: [
        {
            displayName: 'Event',
            name: 'event',
            type: 'options',
            typeOptions: {
                loadOptionsMethod: 'getWebhookEvents',
            },
            
            default: 'order.created',
            required: true,
            description: 'The event to listen to',
        },
        {
            displayName: 'Additional Fields',
            name: 'additionalFields',
            type: 'collection',
            placeholder: 'Add Field',
            default: {},
            options: [
                {
                    displayName: 'Description',
                    name: 'description',
                    type: 'string',
                    default: '',
                    description: 'Description for the webhook',
                },
                {
                    displayName: 'Include Metadata',
                    name: 'includeMetadata',
                    type: 'boolean',
                    default: false,
                    description: 'Whether to include metadata in the webhook payload',
                },
                {
                    displayName: 'Headers',
                    name: 'headers',
                    placeholder: 'Add Header',
                    type: 'fixedCollection',
                    typeOptions: {
                        multipleValues: true,
                    },
                    default: {},
                    options: [
                        {
                            name: 'header',
                            displayName: 'Header',
                            values: [
                                {
                                    displayName: 'Name',
                                    name: 'name',
                                    type: 'string',
                                    default: '',
                                    description: 'Name of the header',
                                },
                                {
                                    displayName: 'Value',
                                    name: 'value',
                                    type: 'string',
                                    default: '',
                                    description: 'Value of the header',
                                },
                            ],
                        },
                    ],
                },
            ],
        },
    ],
};

// Methods to load options dynamically
methods = {
    loadOptions: {
        async getWebhookEvents(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
            LoggerProxy.info('MyCustomNode Webhook: getWebhookEvents method called');
            return [
                
                // Order events
                { name: 'Order Created', value: 'order.created' },
              { name: 'Order Status Changed', value: 'order.status.changed' },

            ];
        },
    }
}

// This method is called when the node is activated (when the workflow is activated)
async activate(this: IHookFunctions): Promise<boolean> {
		LoggerProxy.info('MyCustomNode Webhook: ACTIVATE method called');
		// logic to register the webhook on the 3rd party 
	
        return true;
    } catch (error) {
        LoggerProxy.error('MyCustomNode Webhook: Error in activate method', { error });
        throw new NodeApiError(this.getNode(), error);
    }
}


async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
    LoggerProxy.info('MyCustomNode Webhook: WEBHOOK method called');
    
    try {
        const bodyData = this.getBodyData() as IDataObject;
        const eventType = this.getNodeParameter('event') as string;
        
        // Optional: Add any data transformation specific to the event type
        let processedData: IDataObject = bodyData;
        
        // Example of event-specific processing
        if (eventType === 'order.created' /*&& bodyData.order*/) {
            // Extract just the order data
            //processedData = bodyData.order as IDataObject;
            processedData = bodyData;
        }
        
        return {
            workflowData: [
                this.helpers.returnJsonArray(processedData),
            ],
        };
    } catch (error) {
        LoggerProxy.error('MyCustomNode Webhook node: Error in webhook method', { error });
        throw new NodeApiError(this.getNode(), error);
    }
}
}

</>