n8n-io/n8n · error · Error
Invalid sandbox provider "${String(exhaustiveProvider)}". Se
Error message
Invalid sandbox provider "${String(exhaustiveProvider)}". Set N8N_INSTANCE_AI_SANDBOX_PROVIDER to one of: ${VALID_PROVIDERS.join(', ')}. What it means
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.
Source
Thrown at packages/@n8n/instance-ai/evaluations/harness/sandbox-config.ts:84
if (provider === 'n8n-sandbox') {
const serviceUrl = env.N8N_SANDBOX_SERVICE_URL;
if (!serviceUrl) {
throw new Error(
'N8N_SANDBOX_SERVICE_URL is required for sandbox provider "n8n-sandbox". Set it to the service URL.',
);
}
const apiKey = env.N8N_SANDBOX_SERVICE_API_KEY;
return {
enabled: true,
provider: 'n8n-sandbox',
serviceUrl,
...(apiKey ? { apiKey } : {}),
timeout,
};
}
const exhaustiveProvider: never = provider;
throw new Error(
`Invalid sandbox provider "${String(exhaustiveProvider)}". Set N8N_INSTANCE_AI_SANDBOX_PROVIDER to one of: ${VALID_PROVIDERS.join(', ')}.`,
);
}
function parseTimeout(raw: string | undefined): number | undefined {
if (raw === undefined || raw === '') return undefined;
const n = Number(raw);
if (!Number.isFinite(n) || n <= 0) {
throw new Error(
`N8N_INSTANCE_AI_SANDBOX_TIMEOUT must be a positive number of ms, got "${raw}".`,
);
}
return n;
}
function parsePositiveInt(raw: string | undefined, varName: string): number | undefined {
if (raw === undefined || raw === '') return undefined;
const n = Number(raw);View on GitHub (pinned to 5ac6606e81)
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.
Example fix
// before — new provider 'k8s' added to the union, no branch
// const exhaustiveProvider: never = provider; // compile error
// after — add a branch + VALID_PROVIDERS entry
const VALID_PROVIDERS: SandboxProvider[] = ['n8n-sandbox', 'daytona', 'k8s'];
// ...
if (provider === 'k8s') { return { enabled: true, provider: 'k8s', /* ... */ }; } Defensive patterns
Strategy: type-guard
Validate before calling
import type { SandboxProvider } from '../../src/workspace/create-workspace';
function assertAllProvidersHandled(p: SandboxProvider): never {
// If this compiles, every union member is handled upstream.
throw new Error(`unhandled provider ${p}`);
}
// At the call site, ensure the switch/if-chain covers every member so the
// `never` assignment compiles; add a unit test per provider branch. Type guard
function isKnownSandboxProvider(v: unknown): v is 'n8n-sandbox' | 'daytona' {
return v === 'n8n-sandbox' || v === 'daytona';
} Try / catch
try {
resolveSandboxConfig(process.env);
} catch (e) {
if (e instanceof Error && /Invalid sandbox provider/.test(e.message)) {
// a new provider was added to the union without a branch — add one
}
throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: Extending the SandboxProvider type during feature work and forgetting to handle the new case here; a test casting arbitrary strings to SandboxProvider.
Related errors
- Sandbox "${this.name}" has been destroyed
- Sandbox "${this.name}" failed to start (status: ${this.statu
- Daytona sandbox "${this.id}" is not running
- Workspace has no sandbox
- Sandbox does not support command execution
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/8129818ca18a2693.
Report an issue: GitHub.