Skip to content

bug(lsp): nil pointer dereference in SourceMap #1292

@ptrckmllr

Description

@ptrckmllr

Describe the bug
templ lsp crashes with a panic: runtime error: invalid memory address or nil pointer dereference upon initial opening of a templ file in neovim with nvim/nvim-lspconfig.

To Reproduce
Minimal crashing example, all 3 files in root folder

  • go.mod
    module example.com
    
    go 1.25.2
    
    require github.com/a-h/templ v0.3.960
    
  • go.sum
    github.com/a-h/templ v0.3.960 h1:trshEpGa8clF5cdI39iY4ZrZG8Z/QixyzEyUnA7feTM=
    github.com/a-h/templ v0.3.960/go.mod h1:oCZcnKRf5jjsGpf2yELzQfodLphd2mwecwG4Crk5HBo=
    
  • form.templ
    package ui
    
    templ form()
    

Expected behavior
LSP should not panic.

Logs

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x2 addr=0x20 pc=0x10111dccc]

github.com/a-h/templ/parser/v2.(*SourceMap).SourcePositionFromTarget(...)
	/Users/REDACTED/.local/share/mise/installs/go/1.25.2/packages/pkg/mod/github.com/a-h/templ@v0.3.960/parser/v2/sourcemap.go:123
github.com/a-h/templ/cmd/templ/lspcmd/proxy.Client.PublishDiagnostics({0x1400003e840, {0x1013fb9d0, 0x1400000f230}, 0x1400003e8a0, 0x1400003e8b0}, {0x1013f7c08, 0x14000232c90}, 0x1400070c480)
	/Users/REDACTED/.local/share/mise/installs/go/1.25.2/packages/pkg/mod/github.com/a-h/templ@v0.3.960/cmd/templ/lspcmd/proxy/client.go:72 +0x41c
github.com/a-h/templ/lsp/protocol.clientDispatch({0x1013f7c08, 0x14000232c90}, 0x1400003e840, {0x1013fb958, 0x14000232c60}, 0x1400070c3f0, {0x149a7eca0, 0x1400041b920})
	/Users/REDACTED/.local/share/mise/installs/go/1.25.2/packages/pkg/mod/github.com/a-h/templ@v0.3.960/lsp/protocol/client.go:102 +0x12d0
github.com/a-h/templ/lsp/protocol.NewClient.ClientHandler.func1({0x1013f7c08, 0x14000232c90}, 0x1400070c3f0, {0x149a7eca0, 0x1400041b920})
	/Users/REDACTED/.local/share/mise/installs/go/1.25.2/packages/pkg/mod/github.com/a-h/templ@v0.3.960/lsp/protocol/client.go:36 +0x78
github.com/a-h/templ/lsp/protocol.Handlers.ReplyHandler.func1({0x1013f7c08, 0x14000232c90}, 0x140002fe330, {0x149a7eca0, 0x1400041b920})
	/Users/REDACTED/.local/share/mise/installs/go/1.25.2/packages/pkg/mod/github.com/a-h/templ@v0.3.960/lsp/jsonrpc2/handler.go:35 +0xb4
github.com/a-h/templ/lsp/protocol.Handlers.AsyncHandler.func2.2()
	/Users/REDACTED/.local/share/mise/installs/go/1.25.2/packages/pkg/mod/github.com/a-h/templ@v0.3.960/lsp/jsonrpc2/handler.go:114 +0x6c
created by github.com/a-h/templ/lsp/protocol.Handlers.AsyncHandler.func2 in goroutine 9
	/Users/REDACTED/.local/share/mise/installs/go/1.25.2/packages/pkg/mod/github.com/a-h/templ@v0.3.960/lsp/jsonrpc2/handler.go:112 +0x140

templ info output

❯ templ info
(✓) os [ goos=darwin goarch=arm64 ]
(✓) go [ location=/Users/REDACTED/.local/share/mise/installs/go/1.25.2/bin/go version=go version go1.25.2 darwin/arm64 ]
(✓) gopls [ location=/Users/REDACTED/.local/share/mise/installs/go/1.25.2/bin/gopls version=golang.org/x/tools/gopls v0.20.0 ]
(✓) templ [ location=/Users/REDACTED/.local/share/mise/installs/go/1.25.2/bin/templ version=v0.3.960 ]

Desktop (please complete the following information):

  • OS: macos
  • templ CLI version (templ version): v0.3.960
  • Go version (go version): go version go1.25.2 darwin/arm64
  • gopls version (gopls version): golang.org/x/tools/gopls v0.20.0
  • nvim --version: NVIM v0.12.0-dev-3320+gff777f9a85-Homebrew

Additional context

Logs hinted at an uninitialized/nil SourceMap struct as this lm, ok := sm.TargetLinesToSource[line] map access was cause of the panic.

While looking for code sections where a SourceMap might be accidentally initialized without calling NewSourceMap(), I stumbled upon this code section intempl/cmd/templ/lspcmd/proxy/server.go:291:

generatorOutput, err := generator.Generate(template, w)
if err != nil {
	// It's expected to have some failures while generating code from the template, since
	// you are likely to have invalid docs while you're typing.
	p.Log.Info("generator failure", slog.Any("error", err))
}
p.Log.Info("setting source map cache contents", slog.String("uri", string(uri)))
p.SourceMapCache.Set(string(uri), generatorOutput.SourceMap)

SourceMapCache.Set is called even if the call to generator.Generator() might have errored, causing a nil SourceMap to be set in the cache, which could in turn cause a panic during SourceMap access when it is used from the cache.

I am not certain if this is the actual cause of the panic I experience, either way I assume this should be fixed and I'm willing to provide a PR if you agree. According to the comment it seems to be expected that generator.Generator() might fail here, so an early return like in other sections does seem inappropriate.

  • Would it be a viable approach to put the SourceMapCache.Set call into an else branch instead?
  • Or should we extend the SourceMapCache to no-op during Set if the provided *SourceMap is nil?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions