#4169·gofr

WebSocket handler context is self-referential, causing an unrecoverable stack overflow

Author: om7057Created Sep 8, 2026Updated Sep 8, 2026

What

App.WebSocket's handler builds the context it hands to a handler like this:

go
ctx.Context = context.WithValue(ctx, websocket.WSConnectionKey, conn)

ctx is *gofr.Context, which embeds context.Context. Passing ctx itself (not ctx.Context) as the parent to context.WithValue makes the resulting value-context self-referential: its parent is ctx, and ctx.Context is the very context being constructed.

Impact

Any call that walks the parent chain on this context, such as .Done() or a .Value() lookup for a key other than websocket.WSConnectionKey, recurses into itself forever:

runtime: goroutine stack exceeds 1000000000-byte limit
fatal error: stack overflow

A stack overflow is a fatal error, not a panic, so recover() cannot catch it and the whole process dies. A WebSocket handler that reads any context value other than the WS key (tracing, auth, or any SDK taking a context.Context) hits this.

It currently goes unnoticed because Container.GetConnectionFromContext looks up exactly the one key that returns before recursing.

Where

pkg/gofr/websocket.go, in App.WebSocket's registered handler:

go
ctx.Context = context.WithValue(ctx, websocket.WSConnectionKey, conn)

Suggested fix

Use the embedded context as the parent, not ctx itself:

go
ctx.Context = context.WithValue(ctx.Context, websocket.WSConnectionKey, conn)

Notes

  • Independent of the data race fixed in #4111 (serveWithGoroutine reading c.Context concurrently with this same line reassigning it). That fix stops the parent goroutine from ever calling .Done() on the poisoned context, which closes one crash path, but leaves the self-reference in c.Context itself for the handler and anything downstream of it.
  • Surfaced during review of #4111.