nextjs - 💡(How to fix) Fix `export type` in "use server" file causes runtime ReferenceError in v16.1.x (regression from PR #86296) [1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
vercel/next.js#92460Fetched 2026-04-08 02:59:50
View on GitHub
Comments
1
Participants
2
Timeline
6
Reactions
0
Timeline (top)
labeled ×3closed ×1commented ×1locked ×1

Error Message

In contrast, the validation pass in the same file correctly checks !named.type_only and !s.is_type_only (added in PR #85054). So the validation allows export type, but the registration post-pass still picks it up — causing a runtime error.

Root Cause

PR #86296 ("Fix compilation of exported server functions", merged 2025-11-24, included in v16.1.0) refactored server_actions.rs to collect exports into export_name_by_local_id and then unconditionally register remaining entries as Server Actions in a post-pass:

// Post-pass: registers everything left in export_name_by_local_id as a Server Action
for (id, export_name) in &self.export_name_by_local_id {
    if !self.reference_ids_by_export_name.contains_key(export_name) {
        self.server_reference_exports.push(ServerReferenceExport { ... });
    }
}

However, the collection step that populates export_name_by_local_id does not check is_type_only or type_only:

// Missing check for named_export.type_only and spec.is_type_only
ExportSpecifier::Named(ExportNamedSpecifier {
    orig: ModuleExportName::Ident(orig),
    exported: None,
    ..  // ← is_type_only is silently ignored by the wildcard pattern
}) => {
    self.export_name_by_local_id.insert(orig.to_id(), ...);
}

In contrast, the validation pass in the same file correctly checks !named.type_only and !s.is_type_only (added in PR #85054). So the validation allows export type, but the registration post-pass still picks it up — causing a runtime error.

In v16.0.3, the old code used exported_local_ids (a simple FxHashSet) and only looked up matching functions later. Since a type-only export has no corresponding function, it was naturally skipped. The refactoring in PR #86296 changed this to unconditional registration, breaking the implicit skip behavior.

Fix Action

Fix / Workaround

  • Works correctly in v16.0.3 (before PR #86296)
  • Broke in v16.1.0 (which includes PR #86296)
  • Related: #82029, PR #85054
  • The workaround is to move export type out of "use server" files and import types directly from the source module instead.

Code Example

// Post-pass: registers everything left in export_name_by_local_id as a Server Action
for (id, export_name) in &self.export_name_by_local_id {
    if !self.reference_ids_by_export_name.contains_key(export_name) {
        self.server_reference_exports.push(ServerReferenceExport { ... });
    }
}

---

// Missing check for named_export.type_only and spec.is_type_only
ExportSpecifier::Named(ExportNamedSpecifier {
    orig: ModuleExportName::Ident(orig),
    exported: None,
    ..  // ← is_type_only is silently ignored by the wildcard pattern
}) => {
    self.export_name_by_local_id.insert(orig.to_id(), ...);
}

---

Operating System: macOS 15.5
Node.js: v22.x
Next.js: 16.1.5
RAW_BUFFERClick to expand / collapse

Link to the code that reproduces this issue

Will provide a minimal reproduction repository.

To Reproduce

  1. Create a file with "use server" directive at the top
  2. Add export type { Foo } (re-export a type from another module)
  3. Import the server action function and the type in a client component
  4. Run next build && next start
  5. Navigate to the page that uses the import

Current vs. Expected Behavior

Current (v16.1.0+): Runtime ReferenceError: Foo is not defined — the export type is registered as a Server Action reference in the post-pass of server_actions.rs, but since TypeScript erases the type at compile time, the identifier doesn't exist at runtime.

Expected: export type should be silently ignored, as it was in v16.0.3 and as PR #85054 intended.

Root Cause Analysis

PR #86296 ("Fix compilation of exported server functions", merged 2025-11-24, included in v16.1.0) refactored server_actions.rs to collect exports into export_name_by_local_id and then unconditionally register remaining entries as Server Actions in a post-pass:

// Post-pass: registers everything left in export_name_by_local_id as a Server Action
for (id, export_name) in &self.export_name_by_local_id {
    if !self.reference_ids_by_export_name.contains_key(export_name) {
        self.server_reference_exports.push(ServerReferenceExport { ... });
    }
}

However, the collection step that populates export_name_by_local_id does not check is_type_only or type_only:

// Missing check for named_export.type_only and spec.is_type_only
ExportSpecifier::Named(ExportNamedSpecifier {
    orig: ModuleExportName::Ident(orig),
    exported: None,
    ..  // ← is_type_only is silently ignored by the wildcard pattern
}) => {
    self.export_name_by_local_id.insert(orig.to_id(), ...);
}

In contrast, the validation pass in the same file correctly checks !named.type_only and !s.is_type_only (added in PR #85054). So the validation allows export type, but the registration post-pass still picks it up — causing a runtime error.

In v16.0.3, the old code used exported_local_ids (a simple FxHashSet) and only looked up matching functions later. Since a type-only export has no corresponding function, it was naturally skipped. The refactoring in PR #86296 changed this to unconditional registration, breaking the implicit skip behavior.

Verify canary release

  • I verified that the issue exists in the latest Next.js canary release

Verified on v16.1.5 (stable). Works correctly on v16.0.3.

Provide environment information

Operating System: macOS 15.5
Node.js: v22.x
Next.js: 16.1.5

Which area(s) are affected? (Select all that apply)

Server Actions, SWC transforms (next-custom-transforms)

Which stage(s) are affected? (Select all that apply)

next build (Production)

Additional context

  • Works correctly in v16.0.3 (before PR #86296)
  • Broke in v16.1.0 (which includes PR #86296)
  • Related: #82029, PR #85054
  • The workaround is to move export type out of "use server" files and import types directly from the source module instead.

extent analysis

TL;DR

The most likely fix is to modify the server_actions.rs post-pass to check for is_type_only or type_only before registering exports as Server Actions.

Guidance

  • The issue is caused by the refactoring in PR #86296, which unconditionally registers remaining exports as Server Actions without checking if they are type-only.
  • To verify the issue, create a minimal reproduction repository with a file containing a "use server" directive, an export type statement, and an import of the type in a client component.
  • A potential workaround is to move export type statements out of "use server" files and import types directly from the source module instead.
  • The fix should involve modifying the server_actions.rs post-pass to check for is_type_only or type_only before registering exports as Server Actions, similar to the validation pass.

Example

No code snippet is provided as the issue is related to the Next.js internal implementation and not a user-facing code issue.

Notes

The issue is specific to Next.js versions 16.1.0 and later, and the workaround is to move export type statements out of "use server" files. The fix will likely involve modifying the server_actions.rs file to correctly handle type-only exports.

Recommendation

Apply the workaround by moving export type statements out of "use server" files and importing types directly from the source module instead, as this is a safe and effective solution until the issue is fixed in a future version of Next.js.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

nextjs - 💡(How to fix) Fix `export type` in "use server" file causes runtime ReferenceError in v16.1.x (regression from PR #86296) [1 comments, 2 participants]