Skip to content

An unhandled panic occurred in the Encore compiler: runtime error: invalid memory address or nil pointer dereference #2025

@shimonenator

Description

@shimonenator

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:

  1. 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 } }
>
  1. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions