diff --git a/packages/@n8n/config/src/configs/nodes.config.ts b/packages/@n8n/config/src/configs/nodes.config.ts index d3c43e1f95..396ec352b9 100644 --- a/packages/@n8n/config/src/configs/nodes.config.ts +++ b/packages/@n8n/config/src/configs/nodes.config.ts @@ -61,6 +61,10 @@ export class NodesConfig { @Env('NODES_ERROR_TRIGGER_TYPE') errorTriggerType: string = 'n8n-nodes-base.errorTrigger'; + /** Whether to enable Python execution on the Code node. */ + @Env('N8N_PYTHON_ENABLED') + pythonEnabled: boolean = true; + @Nested communityPackages: CommunityPackagesConfig; } diff --git a/packages/@n8n/config/src/index.ts b/packages/@n8n/config/src/index.ts index 9d4ce0114f..4a41829f35 100644 --- a/packages/@n8n/config/src/index.ts +++ b/packages/@n8n/config/src/index.ts @@ -48,6 +48,7 @@ export { DeploymentConfig } from './configs/deployment.config'; export { MfaConfig } from './configs/mfa.config'; export { HiringBannerConfig } from './configs/hiring-banner.config'; export { PersonalizationConfig } from './configs/personalization.config'; +export { NodesConfig } from './configs/nodes.config'; const protocolSchema = z.enum(['http', 'https']); diff --git a/packages/@n8n/config/test/config.test.ts b/packages/@n8n/config/test/config.test.ts index 73c11a74c6..58e7abfb73 100644 --- a/packages/@n8n/config/test/config.test.ts +++ b/packages/@n8n/config/test/config.test.ts @@ -149,6 +149,7 @@ describe('GlobalConfig', () => { errorTriggerType: 'n8n-nodes-base.errorTrigger', include: [], exclude: [], + pythonEnabled: true, }, publicApi: { disabled: false, diff --git a/packages/nodes-base/nodes/Code/Code.node.ts b/packages/nodes-base/nodes/Code/Code.node.ts index a55eeae603..fe1de04acc 100644 --- a/packages/nodes-base/nodes/Code/Code.node.ts +++ b/packages/nodes-base/nodes/Code/Code.node.ts @@ -1,8 +1,9 @@ -import { TaskRunnersConfig } from '@n8n/config'; +import { NodesConfig, TaskRunnersConfig } from '@n8n/config'; import { Container } from '@n8n/di'; import set from 'lodash/set'; import { NodeConnectionTypes, + UserError, type CodeExecutionMode, type CodeNodeEditorLanguage, type IExecuteFunctions, @@ -21,6 +22,14 @@ import { addPostExecutionWarning, standardizeOutput } from './utils'; const { CODE_ENABLE_STDOUT } = process.env; +class PythonDisabledError extends UserError { + constructor() { + super( + 'This instance disallows Python execution because it has the environment variable `N8N_PYTHON_ENABLED` set to `false`. To restore Python execution, remove this environment variable or set it to `true` and restart the instance.', + ); + } +} + export class Code implements INodeType { description: INodeTypeDescription = { displayName: 'Code', @@ -96,16 +105,20 @@ export class Code implements INodeType { }; async execute(this: IExecuteFunctions) { - const runnersConfig = Container.get(TaskRunnersConfig); - - const nodeMode = this.getNodeParameter('mode', 0) as CodeExecutionMode; - const workflowMode = this.getMode(); - const node = this.getNode(); const language: CodeNodeEditorLanguage = node.typeVersion === 2 ? (this.getNodeParameter('language', 0) as CodeNodeEditorLanguage) : 'javaScript'; + + if (language === 'python' && !Container.get(NodesConfig).pythonEnabled) { + // eslint-disable-next-line n8n-nodes-base/node-execute-block-wrong-error-thrown + throw new PythonDisabledError(); + } + + const runnersConfig = Container.get(TaskRunnersConfig); + const nodeMode = this.getNodeParameter('mode', 0) as CodeExecutionMode; + const workflowMode = this.getMode(); const codeParameterName = language === 'python' ? 'pythonCode' : 'jsCode'; if (runnersConfig.enabled && language === 'javaScript') {