#Crypto Error
21 messages · Page 1 of 1 (latest)
That error is coming from Convex’s custom runtime, which provides the Web Crypto API (crypto) globally rather than allowing you to change or remove it. In the Convex runtime, you can’t delete or re‑define Crypto / crypto, and attempts to do so will throw exactly this kind of error.
From the docs, Convex’s default runtime exposes Web Crypto as a global (crypto, SubtleCrypto, etc.), similar to browser/Cloudflare Workers. [Runtimes]
Given your stack trace:
- It happens inside a workflow (
M(workflow:create)) and an action (A(onboarding/actions:startOnboarding)). - The message
Cannot delete property 'Crypto' of #<io>strongly suggests some code (often a library or polyfill) is trying to delete or monkey‑patch the globalCryptoobject.
From the crypto‑related Discord threads:
- The Convex runtime already has
cryptoglobally; you must not import or polyfill it like in Node. E.g. you should usecrypto.subtle.digest(...)directly, withoutimport crypto from 'node:crypto'. [Crypto thread; Runtimes] - When people tried to use Node’s
cryptoin the Convex runtime, they hit errors or had to move that code behind"use node"actions. [Crypto thread]
Most likely causes in your codebase:
- A polyfill or library that:
- imports Node’s
cryptoor - tries to “shim” Web Crypto by doing something like
delete globalThis.Cryptoor similar.
- imports Node’s
- A polyfill you may have added for another package (e.g. email / React‑Email / MessageChannel fixes) that also touches
cryptoorCrypto.
What to do:
-
Search your Convex code (especially workflows and actions) for
Crypto,crypto, anddeleteand remove any attempts to:- delete
Crypto/crypto - reassign them on
globalThis,self, or similar.
- delete
-
If you’re importing Node’s
cryptoornode:cryptoin Convex runtime files, move that logic into a Node.js action and mark the file with"use node";, or switch to the Web Crypto global:// In default Convex runtime (no imports) const digest = await crypto.subtle.digest("SHA-256", data); -
If this comes from a third‑party library, you’ll likely need to:
- Avoid that library in Convex runtime code, or
- Only call it from a
"use node"action (Node.js runtime), where Node’scryptoand patching are allowed. [Runtimes]
Because the shared sources don’t show this exact Cannot delete property 'Crypto' error, I can’t point to the precise offending line, but it will be something trying to mutate Crypto in your Convex backend code or its dependencies.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@tight snow Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@desert lantern this error comes only from workflow.start even if the workflow itself does nothing
Let me check the knowledge base for you again. One moment! :books:
workflow.start runs the workflow body in the default Convex runtime, not in Node. Internally, workflows are implemented as a mutation and Convex patches globals like console, Math.random, Date, and also restricts crypto‑like APIs to keep execution deterministic. [Workflow usage; Workflow limitations]
Your error:
Cannot delete property 'Crypto' of #<io>
coming only from workflow.start(...) (even if the workflow handler is empty) strongly suggests:
- The error is thrown during the workflow bootstrap, before your handler logic runs.
- Some global patching or a polyfill (in Convex or a dependency) is trying to
delete Cryptoor otherwise mutate theCryptoglobal, which is not allowed in this runtime.
From the runtime docs:
- The default runtime already exposes Web Crypto as a global (
crypto,SubtleCrypto, etc.). You do not need — and must not try — to import/override Node’scryptohere. [Runtimes] - For Node‑only crypto (
node:crypto,createHash, etc.), you must use a"use node"action, not a mutation/workflow. [Node runtime]
And from the crypto Discord thread:
- Using Node’s
cryptoor importingnode:cryptoin the Convex runtime causes bundling/runtime issues; the correct pattern is to either:- use the global
crypto.subtledirectly, or - move the logic into a
"use node"action. [Crypto thread; Runtimes]
- use the global
Given that:
-
Even an “empty” workflow triggers the error, the problem is likely not in the workflow body but in some module that is loaded as part of the workflow bundle (e.g. a shared
utils.ts, polyfills file, or a dependency that touchesCryptoon load). -
Concrete next steps (based on patterns from the crypto thread and workflow docs):
-
Temporarily create a minimal workflow file that:
import { workflow } from "@convex-dev/workflow"; import { v } from "convex/values"; export const testWorkflow = workflow.define({ args: { x: v.number() }, returns: v.null(), handler: async () => { return null; }, });And in some mutation:
import { workflow } from "@convex-dev/workflow"; import { internal } from "./_generated/api"; import { mutation } from "./_generated/server"; export const startTest = mutation({ handler: async (ctx) => { await workflow.start(ctx, internal.testWorkflow, { x: 1 }); }, });If this isolated workflow works, the problem is in your actual workflow’s imported modules or dependencies.
-
In the code that is imported (directly or indirectly) by your real workflow, search for anything that:
- references
Crypto/crypto, or - modifies/deletes properties on
globalThis,self, or similar.
Remove / guard that logic from running in Convex, or move it behind a"use node"action.
- references
-
If you rely on a third‑party SDK that patches crypto (like some auth/email/webhook libs), consider:
- Only using that SDK from a
"use node"action, and - Having the workflow call that action via
step.runAction(...). [Workflow usage; Node runtime]
- Only using that SDK from a
-
The shared sources don’t mention this exact Cannot delete property 'Crypto' of #<io> message, so I can’t point to a specific known bug in Convex itself. Based on what we do know, the key is: workflows run in the deterministic Convex runtime, and any attempt (from your code or a library) to modify or delete Crypto will blow up as soon as the workflow starts, even if its handler is “empty.”
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@tight snow Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@desert lantern export const startOnboarding = authedUserAction({
handler: async (ctx) => {
s: 409,
});
}
// Start the onboarding workflow
const workflowId: WorkflowId = await workflow.start(
ctx,
internal.onboarding.workflows.start,
{
userId: ctx.user.subject,
name: "Placeholder",
},
{
onComplete: internal.onboarding.workflows.cleanup,
context: null, // Explicitly set context to null, otherwise ArgumentValidationError occurs
},
);
return workflowId;
},
});
export const start = workflow.define({
args: zodToConvexFields({
userId: z.string(),
name: z.string(),
}),
returns: zodToConvex(z.string()),
handler: async (step, args): Promise<string> => {
return "";
thats all and i still get the error when calling that action
Let me check the knowledge base for you again. One moment! :books:
The code you’ve shown for startOnboarding and the start workflow looks fine and, by itself, shouldn’t trigger anything related to Crypto. The important detail is that workflows run in a special, deterministic Convex runtime where some globals (including crypto‑related ones) are patched, and any attempt (by your code or a dependency) to modify those globals can crash the workflow at startup. [Workflow limitations; Runtimes]
Your symptoms:
- The error only appears when
workflow.startis called. - The workflow handler is effectively a no‑op (
return ""). - The error mentions
Cannot delete property 'Crypto'.
This strongly indicates:
- The crash happens before your handler runs, while Convex is booting the workflow runtime.
- Some module that is included in the workflow bundle is trying to patch or delete
Crypto/crypto, which is disallowed in that runtime.
From the crypto discussion:
- In the Convex runtime,
cryptois a global, and you should not importnode:cryptoor polyfill it. [Runtimes; Crypto thread] - When Node crypto or shims are used, they must be moved to a
"use node"action. [Node.js runtime]
Given that, the crucial thing is: what else is imported, directly or indirectly, by convex/onboarding/workflows.ts and by any file it imports? Even a “minimal” workflow will still load all those modules when the workflow is bundled and executed.
How to isolate and fix
-
Create a truly isolated test workflow file that imports nothing except Convex & workflow:
// convex/testWorkflow.ts import { workflow } from "@convex-dev/workflow"; import { v } from "convex/values"; export const test = workflow.define({ args: { n: v.number() }, returns: v.string(), handler: async () => { return "ok"; }, });And a small mutation to start it:
// convex/testStart.ts import { mutation } from "./_generated/server"; import { workflow } from "@convex-dev/workflow"; import { internal } from "./_generated/api"; export const startTest = mutation({ handler: async (ctx) => { await workflow.start(ctx, internal.testWorkflow.test, { n: 1 }); }, });If this works, the Convex/workflow system is fine, and the problem is in what your real workflow file imports.
-
Check all imports in
onboarding/workflowsand its transitive imports for anything that:- References
Crypto/crypto. - Imports Node’s
crypto/node:crypto. - Adds polyfills that touch global objects (could be auth, email, webhook, or crypto libs).
If you find such code:
- Remove any
delete globalThis.Crypto,delete (something).crypto, or similar. - If you need Node crypto, move that logic into a
"use node"action file and call it from the workflow viastep.runAction(internal.someAction, ...). [Workflow usage; Node.js runtime]
- References
- Watch for “utility” or polyfill files under
convex/that you might have added for other reasons (e.g. MessageChannel polyfill for React Email / Resend). In other threads people stored polyfills inconvex/polyfills.tsand imported them into Convex functions. [React Email thread]
If any of those polyfills also touchCrypto, they will be loaded into the workflow bundle and can trigger this error, even if the workflow body does nothing.
The provided sources don’t include this exact Cannot delete property 'Crypto' of #<io> error, so I can’t point to a known Convex bug; all available information suggests a bundled dependency or polyfill is trying to mutate the Crypto global in the workflow runtime. The minimal‑workflow test above is the quickest way to confirm that and then narrow it down by trimming imports until the error disappears.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@tight snow Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@desert lantern this throws the same error
Let me check the knowledge base for you again. One moment! :books:
Given that your minimal workflow also throws Cannot delete property 'Crypto' of #<io>, this strongly suggests a problem in the workflow runtime itself or in how the workflow component interacts with Convex’s deterministic runtime, not in your specific handler code.
From the docs:
- Workflows run in the default Convex runtime, not Node, and Convex patches or restricts globals like
cryptoto keep execution deterministic. [Workflow usage; Workflow limitations] - In that runtime, Web Crypto is provided as a global (
crypto,SubtleCrypto, etc.). You shouldn’t import Node’scryptoor otherwise shim it. [Runtimes]
From the crypto Discord thread:
- Attempting to use Node’s crypto (
node:crypto) or importingcryptoin Convex’s default runtime causes bundling/runtime issues; the correct approach is to use the globalcryptoor move crypto‑heavy code into"use node"actions. [Crypto thread; Node runtime] - In at least one case, simply importing from
node:cryptoin a Convex file caused an error like “Could not resolvenode:crypto,” and the fix was to rely on the globalcryptoinstead. [Crypto thread]
However, none of the provided sources mention this exact Cannot delete property 'Crypto' of #<io> message or describe a known bug where any workflow start fails this way. Because your truly minimal workflow (with no extra imports) still throws, that points to:
- Either a bug in the current version of
@convex-dev/workflowwith your Convex backend/runtime version, or - Some global polyfill / patch loaded very early (e.g. via a shared file under
convex/that everything imports) that touchesCryptoin a way the workflow runtime doesn’t tolerate.
The knowledge sources don’t include a fix or confirmed issue matching this error, so I can’t reliably give you a code‑level workaround.
Given that:
- You’ve already reduced it to a minimal
workflow.startcall and still see the error. - The error clearly concerns the
Cryptoglobal inside the deterministic workflow runtime. - There’s no documented public fix or config for this specific error in the sources.
The best next step is to treat this as a likely bug / edge case in the workflow component or runtime and report it:
- Open an issue on the workflow GitHub repo with:
- Your Convex version,
@convex-dev/workflowversion, and runtime info. - The minimal repro (your tiny workflow + mutation that calls
workflow.start).
- Your Convex version,
- Link the error message and mention that it reproduces even when the workflow handler just returns a constant. [Workflow component; Workflow GitHub link]
I’m sorry I can’t provide a concrete in‑project fix: the shared docs and threads simply don’t contain enough information about this particular Crypto deletion error inside workflows to do more than narrow it down to “likely runtime/workflow bug.”
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@tight snow Kapa.ai is still learning and improving, please let me know how I did by reacting below.