Skip to content

TS2883: inferred return types from withAuth() / refreshSession() are non-portable when consumer pins a different @workos-inc/node version #422

Description

@Leestex

Describe the bug

TypeScript emits TS2883 (and on some configurations, the related TS2742) on consumer code that infers return types from withAuth() or refreshSession():

error TS2883: The inferred type of 'requireAuth' cannot be named without a reference to 'User' from
'@workos-inc/authkit-nextjs/node_modules/@workos-inc/node'. This is likely not portable.
A type annotation is necessary.

The error reproduces whenever the consumer's resolved version of @workos-inc/node differs from the version that satisfies authkit-nextjs's transitive ^9.0.0 range. Package managers then install a duplicate nested copy under node_modules/@workos-inc/authkit-nextjs/node_modules/@workos-inc/node/, and TypeScript can no longer write a portable declaration for any inferred type that surfaces User (or OauthTokens, AuthenticationResponse) through the public API.

To Reproduce

  1. In a Next.js project, install @workos-inc/authkit-nextjs@^4.0.1 and pin @workos-inc/node directly to a version that doesn't match ^9.0.0's currently-resolved point version, e.g. 9.2.0 while authkit-nextjs's transitive resolution is still at 9.1.1. With Yarn 4 + node-modules linker this is what dependabot[bot] produces every time it bumps the direct pin.

  2. Write any function whose return type is inferred from withAuth(). A minimal repro:

    // lib/auth/session.ts
    import { withAuth, getSignInUrl } from "@workos-inc/authkit-nextjs"
    import { redirect } from "next/navigation"
    
    export async function requireAuth() {
      const session = await withAuth()
      if (!session.user) {
        const signInUrl = await getSignInUrl()
        redirect(signInUrl)
      }
      return session as typeof session & { user: NonNullable<typeof session.user> }
    }
  3. Run tsc --noEmit (or in any monorepo package with declaration: true / composite: true / isolatedDeclarations).

  4. Observe TS2883 on the requireAuth declaration.

Confirmed in yarn.lock that there are two resolved versions of @workos-inc/node:

"@workos-inc/node@npm:9.2.0":         # consumer direct dep
"@workos-inc/node@npm:^9.0.0":        # transitive from authkit-nextjs → 9.1.1

yarn dedupe --check '@workos-inc/node' reports the split:

@workos-inc/node@npm:^9.0.0 can be deduped from @workos-inc/node@npm:9.1.1 to @workos-inc/node@npm:9.2.0

Expected behavior

tsc should emit a portable declaration. authkit-nextjs's public types should resolve to a single canonical copy of @workos-inc/node's types regardless of the consumer's resolved version, so that any inferred type referencing User / UserInfo / Session / HandleAuthSuccessData is namable from a stable path.

Root cause

@workos-inc/node is declared in dependencies (https://github.com/workos/authkit-nextjs/blob/main/package.json#L41), but its types are surfaced into authkit-nextjs's public-API interfaces in src/interfaces.ts:

import type { AuthenticationResponse, OauthTokens, User } from '@workos-inc/node';

export interface Session  { user: User; ... }
export interface UserInfo { user: User; ... }
export interface HandleAuthSuccessData extends Session {
  oauthTokens?: OauthTokens;
  authenticationMethod?: AuthenticationResponse['authenticationMethod'];
  ...
}

A regular dependencies entry on a SDK whose types appear in the public API allows the package manager to install a separate nested copy whenever the consumer's resolved version diverges. The same hazard exists with next and react — and authkit-nextjs already declares both as peerDependencies for exactly this reason.

Proposed fix

Move @workos-inc/node from dependencies to peerDependencies. The peer-dep contract forces a single resolved copy in the consumer tree by construction. PR coming.

This is a breaking change, but in practice the impact is small: every authkit-nextjs consumer already has to call @workos-inc/node directly for the parts of the WorkOS SDK that authkit doesn't wrap (organization management, user management, FGA, audit logs, etc.).

Workaround (for anyone hitting this today)

yarn dedupe '@workos-inc/node' in the consumer's repo collapses the duplicate, and either a resolutions pin or a CI-side yarn dedupe --check '@workos-inc/node' step (run after install) prevents the regression from recurring on future Dependabot bumps.

Environment

  • authkit-nextjs version: 4.0.1 (also affects all 4.x; same class of bug applied to earlier majors with their own @workos-inc/node ranges)
  • @workos-inc/node versions involved: 9.1.1 (transitive) vs 9.2.0 (consumer direct)
  • Next.js version: any
  • TypeScript version: 6.0.x (also reproduces on 5.x with the same diagnostic code)
  • Package manager: Yarn 4.14.1 with nodeLinker: node-modules (the same hazard exists on npm/pnpm whenever versions split)
  • OS: macOS / Linux (build-time only — not runtime, not browser-dependent)

Additional context

Same root cause manifested as TS2742 on earlier versions before TS6 changed the diagnostic. Closed issue #181 (1.x era) was a related-but-different surfacing of the public-types-export problem.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions