Skip to content

Broken type compatibility with Node.js v24.7.0 and higher #554

Description

@mrkvon

Summary

I use some node:crypto methods (webcrypto) to handle some key-related logic alongside fedify.
When I upgrade @types/node to version >=24.7.0, I start seeing type errors.

This is likely an incompatibility between types used by Fedify (Deno or DOM), and Node.js, which introduces crypto.subtle.decapsulateBits in v24.7.0.

Workaround

  • Don't upgrade @types/node beyond v24.6.2, or
  • Assert results of methods from node:crypto e.g. (await crypto.subtle.importKey(/*....*/)) as CryptoKey.

This issue is not pressing.

Expected Behavior

I'd expect these type errors not to be present.

Actual Behavior

I use some node:crypto methods (webcrypto) to handle some key-related logic.
When I upgrade @types/node to version >=24.7.0, I start seeing type errors like:

error TS2345: Argument of type '(ctx: Context<ContextData>) => Promise<{ privateKey: CryptoKey; publicKey: CryptoKey; }[]>' is not assignable to parameter of type 'ActorKeyPairsDispatcher<ContextData>'.
  Type 'Promise<{ privateKey: CryptoKey; publicKey: CryptoKey; }[]>' is not assignable to type 'CryptoKeyPair[] | Promise<CryptoKeyPair[]>'.
    Type 'Promise<{ privateKey: CryptoKey; publicKey: CryptoKey; }[]>' is not assignable to type 'Promise<CryptoKeyPair[]>'.
      Type '{ privateKey: CryptoKey; publicKey: CryptoKey; }[]' is not assignable to type 'CryptoKeyPair[]'.
        Type '{ privateKey: webcrypto.CryptoKey; publicKey: CryptoKey; }' is not assignable to type 'CryptoKeyPair'.
          The types of 'privateKey.usages' are incompatible between these types.
            Type 'import("node:crypto").webcrypto.KeyUsage[]' is not assignable to type 'KeyUsage[]'.
              Type 'import("node:crypto").webcrypto.KeyUsage' is not assignable to type 'KeyUsage'.
                Type '"decapsulateBits"' is not assignable to type 'KeyUsage'.

61   .setKeyPairsDispatcher(async ctx => {

Environment

OS: Arch Linux
Runtime: Node.js v25

This is a type error, not a runtime error.

Logs / Screenshots

No response

Steps to Reproduce

tbd if needed

Activity

  1. mrkvon commented on Feb 3, 2026

    @mrkvon
    ContributorAuthor

    I start feeling like this might not be a Fedify's problem, unless Fedify claims compatibility with node:crypto methods (which it doesn't, does it?). Please feel free to close this issue if that's the case.

  2. notJoon commented on Feb 3, 2026

    @notJoon
    Contributor

    I remember there were similar issues before (maybe #423 ?). The related code will need to be updated

  3. 2chanhaeng commented on Feb 16, 2026

    @2chanhaeng
    Member

    The extended methods standardized. So I think it seems best for us to just apply type assertions for now, sit back, and enjoy a cup of tea. 🍵

  4. added theissue type on May 19, 2026
  5. changed the title [-]Broken type compatibility with node v24.7.0 and higher[/-] [+]Broken type compatibility with Node.js v24.7.0 and higher[/+] on Oct 29, 2026
  6. self-assigned this
    on Nov 5, 2026
  7. dahlia commented on Nov 5, 2026

    @dahlia
    Member

    Thanks for the detailed report. I can confirm the problem is still there.

    It reproduces on 2.0-maintenance (2.0.36), on 2.4-maintenance (2.4.5), and on main. With @types/node 24.6.0 the same consumer code compiles; with 24.7.0 and later it fails with the encapsulateBits/decapsulateBits mismatch, under both TypeScript 6.0 and 7.1.

    The cause is that our public declarations use the ambient CryptoKey and CryptoKeyPair types without importing them, so the consumer's lib setting decides which declaration those names resolve to. Since Node.js 24.7, webcrypto.KeyUsage also includes the ML-KEM usages, which makes the Node.js union a superset of the DOM and Web Worker unions. usages is a mutable array, so webcrypto.KeyUsage[] is not assignable to KeyUsage[]. The incompatibility only goes one way: keys returned by Fedify still fit into node:crypto slots, and nothing is wrong at run time.

    This also means casts inside Fedify would not fix it. The error appears where application code passes its own keys to one of our functions, so the declared parameter types have to change.

    What I would like to do is give Fedify its own structural input types, CryptoKeyLike and CryptoKeyPairLike, in @fedify/vocab-runtime, and widen only the input positions: ActorKeyPairsDispatcher, SharedInboxKeyDispatcher, SenderKeyPair, the signing and proof helpers, exportJwk(), the key export helpers in @fedify/vocab-runtime, and the publicKey inputs of CryptographicKey and Multikey. Return types would stay as the ambient CryptoKey and CryptoKeyPair, so keys that Fedify produces can still be passed to crypto.subtle.* without a cast. Inside Fedify, we narrow once at the crypto.subtle boundary.

    Something like this:

    export interface CryptoKeyLike {
      readonly algorithm: { readonly name: string };
      readonly extractable: boolean;
      readonly type: string;
      readonly usages: readonly string[];
    }
    
    export interface CryptoKeyPairLike {
      readonly privateKey: CryptoKeyLike;
      readonly publicKey: CryptoKeyLike;
    }

    type: string and usages: readonly string[] are deliberate. Cloudflare Workers declares CryptoKey.type as string, so a literal union would break Workers users, while the readonly string array accepts both the Node.js and DOM usage unions. I checked both cases against the Workers type definitions.

    Waiting for lib.dom.d.ts to catch up does not look like a good option either: the TypeScript DOM lib and @types/node are versioned separately, so the mismatch can come back whenever either side adds a usage.

    The change is source-compatible for callers, so I would land it on 2.0-maintenance first and forward-port it through the later maintenance branches, then main and next. ActorKeyPairsDispatcher would get a defaulted type parameter (TKeyPair extends CryptoKeyPairLike = CryptoKeyPair) so that code calling a dispatcher directly keeps its narrow return type.

    The existing mise run check:types cannot catch this, because the repository's own projects type-check with the webworker lib and never pass node:crypto keys into the API. I will add a consumer fixture that compiles with lib: ["esnext", "dom"], types: ["node"], skipLibCheck: false, and @types/node pinned at 24.7 or later, exercising the callbacks, the signing helpers, and the vocabulary constructors without casts.

    I will open a pull request against 2.0-maintenance with the fix and the regression fixture.

  8. linked a pull request that will close this issueAccept `node:crypto` keys in public APIs #1391on Nov 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

Fields

Priority

None yet

Effort

None yet

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions