Skip to content

textDocument/completion runtime error: invalid memory address or nil pointer dereference #2580

@sanny-io

Description

@sanny-io

Stack trace

2026-01-24 10:30:58.816 [error] panic handling request textDocument/completion: runtime error: invalid memory address or nil pointer dereference
goroutine 40293 [running]:
runtime/debug.Stack()
	runtime/debug/stack.go:26 +0x64
github.com/microsoft/typescript-go/internal/lsp.(*Server).recover(0x40001b6e08, {0x861a0?, 0x4005051500?}, 0x400d5bc660)
	github.com/microsoft/typescript-go/internal/lsp/server.go:777 +0x40
panic({0x77e1e0?, 0x12b5a00?})
	runtime/panic.go:783 +0x120
github.com/microsoft/typescript-go/internal/ast.(*Node).IsTypeOnly(...)
	github.com/microsoft/typescript-go/internal/ast/ast.go:923
github.com/microsoft/typescript-go/internal/ls.(*LanguageService).getImportStatementCompletionInfo(0x401e94f280, 0x40161cb740, 0x4003e343c0?)
	github.com/microsoft/typescript-go/internal/ls/completions.go:5110 +0x408
github.com/microsoft/typescript-go/internal/ls.(*LanguageService).getCompletionData(0x401e94f280, {0xc422a8, 0x4013391860}, 0x401ab6e608, 0x4013387508, 0xa7, 0x40076b9a00)
	github.com/microsoft/typescript-go/internal/ls/completions.go:528 +0x25c
github.com/microsoft/typescript-go/internal/ls.(*LanguageService).getCompletionsAtPosition(0x401e94f280, {0xc422a8, 0x4013391860}, 0x4013387508, 0xa7, 0x0)
	github.com/microsoft/typescript-go/internal/ls/completions.go:347 +0x284
github.com/microsoft/typescript-go/internal/ls.(*LanguageService).ProvideCompletion(0x401e94f280, {0xc422a8, 0x40133917a0}, {0x40000ce080?, 0x40133917a0?}, {0xce080?, 0x40?}, 0x4011d69370)
	github.com/microsoft/typescript-go/internal/ls/completions.go:47 +0xd0
github.com/microsoft/typescript-go/internal/lsp.(*Server).handleCompletion(0x4021c0a7e0?, {0xc422a8?, 0x40133917a0?}, 0x40000ce080?, 0xffff9c527f30?)
	github.com/microsoft/typescript-go/internal/lsp/server.go:1095 +0x3c
github.com/microsoft/typescript-go/internal/lsp.init.func1.registerLanguageServiceWithAutoImportsRequestHandler[...].28({0xc422a8, 0x40133917a0}, 0x400d5bc660)
	github.com/microsoft/typescript-go/internal/lsp/server.go:682 +0xf4
github.com/microsoft/typescript-go/internal/lsp.(*Server).handleRequestOrNotification(0x40001b6e08, {0xc422e0?, 0x40151938b0?}, 0x400d5bc660)
	github.com/microsoft/typescript-go/internal/lsp/server.go:531 +0x170
github.com/microsoft/typescript-go/internal/lsp.(*Server).dispatchLoop.func1()
	github.com/microsoft/typescript-go/internal/lsp/server.go:414 +0x34
created by github.com/microsoft/typescript-go/internal/lsp.(*Server).dispatchLoop in goroutine 6
	github.com/microsoft/typescript-go/internal/lsp/server.go:438 +0x7d8

2026-01-24 10:30:58.816 [error] Request textDocument/completion failed.
  Message: InternalError: panic handling request textDocument/completion: runtime error: invalid memory address or nil pointer dereference
  Code: -32603

Steps to reproduce

I was typing right here at the top. Seems pretty consistent to reproduce unlike other crashes I've experienced. Crashes right when I type the r in super

import type { CacheInvalidationOptions, CacheProvider, CacheEntry } from '../types'
import { Redis } from 'ioredis'
import { getTotalTtl } from '../utils'
import super // ⚠️ I was typing here

export class RedisCacheProvider implements CacheProvider {
  private readonly redis: Redis

  constructor(options: RedisCacheProviderOptions) {
    this.redis = new Redis(options.url)
  }

  async get(key: string) {
    const entryJson = await this.redis.get(formatQueryKey(key))

    if (!entryJson) {
      return undefined
    }

    return JSON.parse(entryJson) as CacheEntry
  }

  async set(key: string, entry: CacheEntry) {
    const multi = this.redis.multi()
    const formattedKey = formatQueryKey(key)

    multi.set(formattedKey, JSON.stringify(entry))

    const totalTtl = getTotalTtl(entry)

    if (totalTtl > 0) {
      multi.expire(formattedKey, totalTtl)
    }

    if (entry.options.tags) {
      for (const tag of entry.options.tags) {
        const formattedTagKey = formatTagKey(tag)

        multi.sadd(formattedTagKey, formattedKey)

        if (totalTtl > 0) {
          multi.expire(formattedTagKey, totalTtl, 'GT')
          multi.expire(formattedTagKey, totalTtl, 'NX')
        }
      }
    }

    await multi.exec()
  }

  async invalidate(options: CacheInvalidationOptions) {
    if (options.tags && options.tags.length > 0) {
      await Promise.all(
        options.tags.map(tag => {
          return new Promise((resolve, reject) => {
            const stream = this.redis.sscanStream(formatTagKey(tag), {
              count: 100,
            })

            stream.on('data', async (keys: string[]) => {
              if (keys.length > 1) {
                await this.redis.del(...keys)
              }
            })

            stream.on('error', reject)
            stream.on('end', resolve)
          })
        }),
      )
    }
  }

  async invalidateAll() {
    await new Promise((resolve, reject) => {
      const stream = this.redis.scanStream({
        count: 100,
        match: 'zenstack:cache:*',
      })

      stream.on('data', async (keys: string[]) => {
        if (keys.length > 1) {
          await this.redis.del(...keys)
        }
      })

      stream.on('error', reject)
      stream.on('end', resolve)
    })
  }
}

export type RedisCacheProviderOptions = {
  url: string
}

function formatQueryKey(key: string) {
  return `zenstack:cache:query:${key}`
}

function formatTagKey(key: string) {
  return `zenstack:cache:tag:${key}`
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions