microsoft / microsoft/TypeScript

Panic "Diagnostic emitted without context" ts-go in declaration emit for `export default` arrow/function expression with non-portable inferred return type

Open
#63,761 1 comment 2 reactions 3 assignees Claimed by @RyanCavanaugh View on GitHub
Bug
Dominant language
Go
Stars
111k
Forks
14.3k
PR merge metrics
PR metrics pending

Description

### 🔎 Search Terms

panic Diagnostic emitted without context, declaration emit crash, throwDiagnostic, handleSymbolAccessibilityError, transformExportAssignment, canProduceDiagnostics, export default arrow function, function expression, inferred return type cannot be named, symbol accessibility, TS4082, tsgo, native compiler

### 🕗 Version & Regression Information

- This changed between versions 6.0.3 and 7.0.0 the JS compiler emits TS4082 for this code; the native compiler panics
- This changed in commit or PR: same code path as [microsoft/typescript-go#4629](https://github.com/microsoft/typescript-go/issues/4629) ("Unknown parent for parameter: KindArrowFunction"), whose fix PR [microsoft/typescript-go#4648](https://github.com/microsoft/typescript-go/pull/4648) (merged 2026-07-15) covered the parameter-diagnostics panic but not this return-type-serialization panic which still reproduces on [`typescript@next` 7.1.0-dev.20260819.1](https://www.npmjs.com/package/typescript/v/7.1.0-dev.20260819.1)
- This is the behavior in every version I tried ([`typescript@7.0.2`](https://www.npmjs.com/package/typescript/v/7.0.2), [`@typescript/native-preview` 7.0.0-dev.20260707.2](https://www.npmjs.com/package/@typescript/native-preview), [`typescript@next` 7.1.0-dev.20260819.1](https://www.npmjs.com/package/typescript/v/7.1.0-dev.20260819.1)), and I reviewed the FAQ for entries about declaration emit and "has or is using private name" errors — none cover compiler panics
- I was unable to test this on versions before 7.0 because the panicking code path (native declaration emit's promotion of default-exported function expressions) does not exist in the JS compiler

### ⏯ Playground Link

_No response_

### 💻 Code

```ts
// @filename: helper.ts
declare const brand: unique symbol;

class Foo {
private [brand]: number = 1;
}

export function makeFoo() {
return new Foo();
}

// @filename: repro.ts
import { makeFoo } from './helper';

export default () => makeFoo();
```

```jsonc
// tsconfig.json
{ "compilerOptions": { "declaration": true, "emitDeclarationOnly": true, "outDir": "dist", "strict": true } }
```

Run `tsc -p .`.

Both ingredients are required: the default export must be an **ExportAssignment whose expression is an
arrow function or function expression** (`export default function () {}` at statement level is a
function *declaration* a different AST node and errors correctly), and the **inferred return type**
must contain a symbol that cannot be named from the exporting module.

### 🙁 Actual behavior

The whole compile crashes with a Go panic (exit code 2) instead of reporting a diagnostic:

```
panic: Diagnostic emitted without context [recovered, repanicked]

github.com/microsoft/typescript-go/internal/transformers/declarations.throwDiagnostic(...)
internal/transformers/declarations/transform.go:276
...(*SymbolTrackerImpl).handleSymbolAccessibilityError(...) // Accessibility = CannotBeNamed, ErrorNode = nil
internal/transformers/declarations/tracker.go:211
...(*NodeBuilderImpl).serializeReturnTypeForSignature
...(*DeclarationTransformer).ensureType transform.go:1647
...(*DeclarationTransformer).transformFunctionLikeToDeclaration
...(*DeclarationTransformer).transformExportAssignment transform.go:1223
```

In a large monorepo this also fires under `noEmit` + `incremental` (declaration transforms run for
buildinfo state), where transient stale-cache states can create the cannot be named condition on
otherwise-clean code making the crash appear nondeterministic.

### 🙂 Expected behavior

A normal diagnostic, as TypeScript 6.0.3 emits for the identical input:

```
repro.ts(3,1): error TS4082: Default export of the module has or is using private name 'brand'.
```

tsgo itself handles every neighbouring form correctly — only the expression-form default export panics:

| form | result (tsgo 7.0.2 / nightly) |
| --- | --- |
| `export default () => makeFoo();` | **panic** |
| `export default (function () { … });` | **panic** |
| `export default function () { … }` (declaration) | TS4058 ✓ |
| `export const make = () => makeFoo();` | TS4023 + TS4094 ✓ |

### Additional information about the issue

Root cause (verified at tag `typescript/v7.0.2`, commit 2bd066d8, and unchanged on current `main`):

1. `visitSourceFile` installs `throwDiagnostic` literally `panic("Diagnostic emitted without
context")` as the file-level default `getSymbolAccessibilityDiagnostic`
(internal/transformers/declarations/transform.go:276, :289).
2. `transformExportAssignment`'s `ast.IsFunctionLike(unwrapped)` branch (transform.go ~1241 on main),
which promotes `export default ` via
`transformFunctionLikeToDeclaration → ensureType(unwrapped)`, returns **without** installing a
diagnostic context and **without** `PushErrorFallbackNode` unlike the non-function fallback
branch just below it, which installs `Default_export_of_the_module_has_or_is_using_private_name_0`
before serializing.
3. `ensureType` installs a per-node handler only `if canProduceDiagnostics(node)` (transform.go ~1671
on main), and `canProduceDiagnostics` (internal/transformers/declarations/util.go:25) does not
include `ast.IsArrowFunction` / `ast.IsFunctionExpression`.

So a `SymbolAccessibilityCannotBeNamed` result raised while serializing the arrow's inferred return
type reaches the panic stub and takes down the compiler.

Suggested minimal fix: in the `IsFunctionLike` branch, install the same default-export diagnostic
context (and `PushErrorFallbackNode(assignment)` / `Pop`) that the sibling fallback branch already
uses, before calling `transformFunctionLikeToDeclaration`. This restores parity with TS 6.x (TS4082).

Prior occurrences of the same panic via other triggers: microsoft/typescript-go#2522 (CommonJS `.cjs`
re-export — fixed for that trigger only), microsoft/typescript-go#3378 (LSP, closed unverified).
Observed in the wild: supabase/supabase#48876 (worked around by deleting the triggering code).

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.