-
Notifications
You must be signed in to change notification settings - Fork 489
Description
Problem
Encore v1.48.12 (and earlier versions) is encountering the following error when generating TS client:
encore gen client --lang typescript --output ../frontend/client.ts
error:
── Internal compiler error ────────────────────────────────────────────────────────────────[E0001]──
An unhandled panic occurred in the Encore compiler: runtime error: invalid memory address or nil
pointer dereference
This is a bug in Encore and should not have occurred. Please report this issue to the Encore team
either on Github at https://github.com/encoredev/encore/issues/new and include this error.
╭─[Stack Trace]
├─▶ clientgen.Client.func1 encr.dev/pkg/clientgen/client.go:65
├─▶ clientgen.(*typescript).handleBailout encr.dev/pkg/clientgen/typescript.go:1877
├─▶ clientgen.(*typescript).isRecursive encr.dev/pkg/clientgen/typescript.go:1985
├─▶ clientgen.(*typescript).writeTyp encr.dev/pkg/clientgen/typescript.go:1835
├─▶ clientgen.(*typescript).writeService encr.dev/pkg/clientgen/typescript.go:463
├─▶ clientgen.(*typescript).Generate encr.dev/pkg/clientgen/typescript.go:109
╰─[... remaining frames omitted ...]
Workarounds
After very lengthy debugging off effectively disabling all services and routes, a few workarounds were found:
- Inline types, avoiding generic types and nesting:
export interface CreateItemDto {
name: string & (MinLen<1> & MaxLen<100>);
}
export interface SingleResponse<T> {
data: T;
}
export type ItemResponseDto = {
id: string;
name: string;
};
// This causes nil pointer dereference
export const createItem = api<
CreateItemDto,
SingleResponse<ItemResponseDto>
>
// This works
export const createItem = api<
CreateItemDto,
{ data: { id: string; name: string } }
>
- Use
--ts:shared-types
flag
Root Cause
Both Gemini and Claude concluded that the error is coming from the isRecursive
function in typescript.go:1985:
Claude's take:
The problem is in the isRecursive
function at line 1985 in typescript.go
. Looking at the source code I examined earlier, this function checks for circular references in type definitions:
func (ts *typescript) isRecursive(typ *schema.Type) bool {
// Treat recursively seen types as if they are optional
recursiveType := false
if n := typ.GetNamed(); n != nil {
recursiveType = ts.typs.IsRecursiveRef(ts.currDecl.Id, n.Id)
}
return recursiveType
}
The issue is that ts.currDecl
is nil
when isRecursive
is called. This happens when the TypeScript generator is processing a type but currDecl
hasn't been properly set.
Gemini's take:
Based on the stack trace, the panic occurs in the TypeScript client generator, specifically within a function called isRecursive
. This suggests the generator is failing to correctly handle a recursive type definition in one of your Encore services. The nil pointer dereference
error likely means it's trying to access a part of the type information that doesn't exist, which can happen with complex or malformed recursive type definitions.
My hypothesis is that one of your services exposes a data structure with a circular reference that the generator isn't built to handle.
The key issue lies in this function:
// encr.dev/pkg/clientgen/typescript.go
func (ts *typescript) isRecursive(typ *schema.Type) bool {
// Treat recursively seen types as if they are optional
recursiveType := false
if n := typ.GetNamed(); n != nil {
recursiveType = ts.typs.IsRecursiveRef(ts.currDecl.Id, n.Id)
}
return recursiveType
}
The function uses ts.currDecl
to determine the current context for checking recursive types. However, ts.currDecl
is only set when the generator processes a type definition within a service. If a service has public API endpoints but no dedicated type definitions of its own, ts.currDecl
can be nil
when isRecursive
is called, leading to the "nil pointer dereference" panic.