{"record":{"id":"8129818ca18a2693","repo":"n8n-io/n8n","slug":"invalid-sandbox-provider-string-exhaustiveprovi","errorCode":null,"errorMessage":"Invalid sandbox provider \"${String(exhaustiveProvider)}\". Set N8N_INSTANCE_AI_SANDBOX_PROVIDER to one of: ${VALID_PROVIDERS.join(', ')}.","messagePattern":"Invalid sandbox provider \"(.+?)\"\\. Set N8N_INSTANCE_AI_SANDBOX_PROVIDER to one of: (.+?)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@n8n/instance-ai/evaluations/harness/sandbox-config.ts","lineNumber":84,"sourceCode":"\tif (provider === 'n8n-sandbox') {\n\t\tconst serviceUrl = env.N8N_SANDBOX_SERVICE_URL;\n\t\tif (!serviceUrl) {\n\t\t\tthrow new Error(\n\t\t\t\t'N8N_SANDBOX_SERVICE_URL is required for sandbox provider \"n8n-sandbox\". Set it to the service URL.',\n\t\t\t);\n\t\t}\n\t\tconst apiKey = env.N8N_SANDBOX_SERVICE_API_KEY;\n\t\treturn {\n\t\t\tenabled: true,\n\t\t\tprovider: 'n8n-sandbox',\n\t\t\tserviceUrl,\n\t\t\t...(apiKey ? { apiKey } : {}),\n\t\t\ttimeout,\n\t\t};\n\t}\n\n\tconst exhaustiveProvider: never = provider;\n\tthrow new Error(\n\t\t`Invalid sandbox provider \"${String(exhaustiveProvider)}\". Set N8N_INSTANCE_AI_SANDBOX_PROVIDER to one of: ${VALID_PROVIDERS.join(', ')}.`,\n\t);\n}\n\nfunction parseTimeout(raw: string | undefined): number | undefined {\n\tif (raw === undefined || raw === '') return undefined;\n\tconst n = Number(raw);\n\tif (!Number.isFinite(n) || n <= 0) {\n\t\tthrow new Error(\n\t\t\t`N8N_INSTANCE_AI_SANDBOX_TIMEOUT must be a positive number of ms, got \"${raw}\".`,\n\t\t);\n\t}\n\treturn n;\n}\n\nfunction parsePositiveInt(raw: string | undefined, varName: string): number | undefined {\n\tif (raw === undefined || raw === '') return undefined;\n\tconst n = Number(raw);","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/instance-ai/evaluations/harness/sandbox-config.ts#L66-L102","documentation":"This is a compile-time exhaustiveness guard. The line `const exhaustiveProvider: never = provider` only compiles when every SandboxProvider union member has been handled in an earlier branch. If a new provider is added to the SandboxProvider type without a matching branch, the `never` assignment fails to compile; if it somehow reaches runtime (e.g. via unsafe casts defeating narrowing), this throw fires as a defensive fallback.","triggerScenarios":"A new SandboxProvider literal was added to the union but no `if (provider === ...)` branch was added; the provider value was injected via an `as` cast that bypassed the VALID_PROVIDERS check at the top of the function.","commonSituations":"Extending the SandboxProvider type during feature work and forgetting to handle the new case here; a test casting arbitrary strings to SandboxProvider.","solutions":["Add a dedicated `if (provider === '<new-provider>')` branch returning the right SandboxConfig shape.","Add the new provider to VALID_PROVIDERS so the upfront guard accepts it.","Remove any `as SandboxProvider` casts in callers that smuggle in unrecognised values."],"exampleFix":"// before — new provider 'k8s' added to the union, no branch\n// const exhaustiveProvider: never = provider; // compile error\n\n// after — add a branch + VALID_PROVIDERS entry\nconst VALID_PROVIDERS: SandboxProvider[] = ['n8n-sandbox', 'daytona', 'k8s'];\n// ...\nif (provider === 'k8s') { return { enabled: true, provider: 'k8s', /* ... */ }; }","handlingStrategy":"type-guard","validationCode":"import type { SandboxProvider } from '../../src/workspace/create-workspace';\n\nfunction assertAllProvidersHandled(p: SandboxProvider): never {\n  // If this compiles, every union member is handled upstream.\n  throw new Error(`unhandled provider ${p}`);\n}\n\n// At the call site, ensure the switch/if-chain covers every member so the\n// `never` assignment compiles; add a unit test per provider branch.","typeGuard":"function isKnownSandboxProvider(v: unknown): v is 'n8n-sandbox' | 'daytona' {\n  return v === 'n8n-sandbox' || v === 'daytona';\n}","tryCatchPattern":"try {\n  resolveSandboxConfig(process.env);\n} catch (e) {\n  if (e instanceof Error && /Invalid sandbox provider/.test(e.message)) {\n    // a new provider was added to the union without a branch — add one\n  }\n  throw e;\n}","preventionTips":["When adding a SandboxProvider member, update VALID_PROVIDERS, add a branch, and add a test in the same PR.","Rely on the `never` assignment as a compile-time exhaustiveness check; keep it.","Avoid `as SandboxProvider` casts at the boundary; validate first."],"tags":["typescript","exhaustiveness","sandbox","never-type"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}