Skip to content

fix(graphql): return gin context error uniformly across all resolvers#638

Merged
lakhansamani merged 3 commits into
mainfrom
fix/gin-context-error-uniform
Jun 17, 2026
Merged

fix(graphql): return gin context error uniformly across all resolvers#638
lakhansamani merged 3 commits into
mainfrom
fix/gin-context-error-uniform

Conversation

@lakhansamani

Copy link
Copy Markdown
Contributor

Summary

  • All 50 GraphQL resolver files used gc, _ := utils.GinContextFromContext(ctx), silently discarding the error from missing gin context
  • Apply the pattern already established in signup.go uniformly: return the actual error immediately instead of ignoring it
  • Resolvers that call ApplyToGin(gc, side) were already at risk of a nil-pointer panic if gin context was absent; this removes that risk entirely
  • Resolvers that only pass gc to MetaFromGin now return a clear error instead of silently producing empty request metadata

Behaviour change

Before: missing gin context → MetaFromGin(nil) → empty RequestMetadata → downstream auth failure with a generic "unauthorized" message, or nil-pointer panic in resolvers calling ApplyToGin.

After: missing gin context → resolver returns the actual "could not retrieve gin.Context" error immediately.

Test plan

  • go build ./... passes (verified locally)
  • Existing integration tests pass: go clean --testcache && TEST_DBS=sqlite go test -p 1 ./internal/integration_tests/...

All 50 GraphQL resolver files previously used `gc, _ :=
utils.GinContextFromContext(ctx)`, silently discarding the error and
relying on MetaFromGin's nil safety. This is fine at runtime but masks
programming errors and leaks an inconsistent contract: resolvers that
call ApplyToGin(gc, side) would panic on a nil gc, while read-only
resolvers would silently produce empty request metadata.

Apply the pattern already established in signup.go uniformly: check the
error from GinContextFromContext and return it immediately. The caller
gets the actual error ("could not retrieve gin.Context") instead of a
downstream auth failure or a nil-pointer panic.
For all 51 GraphQL resolvers the previous error branch was:
  return nil, err
Now it is:
  g.Log.Debug().Err(err).Msg("failed to get gin context")
  metrics.RecordSecurityEvent(metrics.SecurityEventGinContextMissing, "graphql")
  return nil, err

Changes:
- internal/metrics/metrics.go: add SecurityEventGinContextMissing constant
- internal/graphql/*.go (51 files): add structured debug log and security
  metric to the gin context error branch; add metrics import
- internal/integration_tests/gin_context_missing_test.go: 9 subtests
  covering resolvers with/without ApplyToGin and with/without auth,
  each called with context.Background() (no embedded gin.Context)

Not affected:
- HTTP handlers receive gin.Context directly as a function parameter
- gRPC handlers use transport.MetaFromGRPC which degrades gracefully
  when no gRPC metadata is present (already tested in
  internal/grpcsrv/transport/grpc_metadata_test.go TestMetaFromGRPC_NoMetadata)
## GORM stdout → stderr (fixes smoke mcp_stdio)

GORM's default logger writes to os.Stdout. The MCP server speaks
JSON-RPC over stdio, so any stray stdout line (slow-query warnings,
AutoMigrate errors) corrupts the stream and produces
"unexpected end of JSON input" on the client.

PR #632 scoped logger.Discard to clearLegacyColumnUniqueness but the
global gorm.Config had no Logger set, leaving AutoMigrate and all other
GORM operations on the default stdout logger.

Fix: wire a stderr-backed GORM logger into ormConfig so every GORM
operation routes to stderr rather than stdout. Slow-query warnings and
errors remain visible; stdout stays clean for stdio transports.

## context.WithoutCancel in fire-and-forget goroutines

18 goroutines across 9 service files were capturing the request ctx and
using it for EventsProvider.RegisterEvent, StorageProvider.AddSession,
etc. If the request context is cancelled (client disconnect, upstream
timeout), those goroutines would return early without firing webhooks or
persisting sessions.

Fix: inject ctx := context.WithoutCancel(ctx) as the first statement in
every goroutine that uses the request ctx. This preserves any context
values (trace IDs, logger) while detaching from the cancellation signal,
so side effects complete independently of the request lifetime.

Goroutines that do not use ctx (MemoryStoreProvider calls, EmailProvider
calls whose signature takes no context) are left unchanged.
@lakhansamani lakhansamani merged commit 6900573 into main Jun 17, 2026
4 checks passed
@lakhansamani lakhansamani deleted the fix/gin-context-error-uniform branch June 17, 2026 05:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant