{"record":{"id":"a4b2439c0628a3e3","repo":"Billionmail/BillionMail","slug":"request-context-is-nil","errorCode":null,"errorMessage":"request context is nil","messagePattern":"request context is nil","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/askai/openai.go","lineNumber":733,"sourceCode":"\t\t}\n\t\t// Recursively call the function to handle tool calls\n\t\treturn o.CreateChatCompletionStream(request, req, isText)\n\t}\n\n\t// Ensure the response is properly closed\n\to.WriteHtml()                           // Write HTML content if the chat has ended\n\to.WriteMessage(o.Content, finishReason) // Write the final message to the chat\n\to.WriteEvent(request, \"\", isText, true) // Write final event to indicate completion\n\n\treturn nil\n}\n\nfunc (o *OpenAI) Chat(content string, isText bool) error {\n\t// Implementation for sending a chat message to OpenAI\n\t// This function should handle the logic for sending a chat message and return the response or any error encountered\n\trequest := ghttp.RequestFromCtx(o.Ctx)\n\tif request == nil {\n\t\treturn errors.New(\"request context is nil\")\n\t}\n\n\tif o.Client == nil {\n\t\to.GetClient()\n\t}\n\n\ttools := []openai.Tool{\n\t\to.RegisterWebSearchTool(), // Register the web search tool for HTTP requests\n\t}\n\n\to.UserContent = content // Set the user content to the provided content\n\n\treq := openai.ChatCompletionRequest{\n\t\tModel:               o.ModelId,\n\t\tMaxCompletionTokens: o.MaxTokens,\n\t\tMessages:            o.GetMessages(), // Get the messages for the chat\n\t\tTemperature:         0.6,             // Set temperature for the chat completion\n\t\tStream:              true,","sourceCodeStart":715,"sourceCodeEnd":751,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/askai/openai.go#L715-L751","documentation":"Chat() resolves the HTTP request from the stored context via ghttp.RequestFromCtx(o.Ctx); if o.Ctx carries no GoFrame request (nil), it cannot write streaming events, so it returns this error before doing any work.","triggerScenarios":"Calling OpenAI.Chat() with o.Ctx unset, with a plain context.Background(), or from a background goroutine/worker where the original ghttp request context is not propagated.","commonSituations":"Invoking Chat from a queue consumer or cron job; spawning a goroutine with a detached context; constructing OpenAI manually in tests without a GoFrame handler context; middleware that replaced the context.","solutions":["Only call Chat() from within a GoFrame HTTP handler where gctx/ghttp context is available","Propagate the original request context into goroutines (go func(ctx){...}(o.Ctx)) instead of context.Background()","Set o.Ctx from the handler's request context before calling Chat","Refactor Chat to accept an event writer abstraction so it can run without an ghttp.Request"],"exampleFix":"// before\nworker := o\no.Ctx = context.Background() // request lost\ngo worker.Chat(prompt, true)\n// after\n// in the HTTP handler:\no.Ctx = r.Context() // ghttp request context\ngo func(ctx context.Context) { o.Chat(prompt, true) }(o.Ctx)","handlingStrategy":"type-guard","validationCode":"if ghttp.RequestFromCtx(o.Ctx) == nil {\n    return errors.New(\"Chat requires an active ghttp request context\")\n}","typeGuard":"func hasHttpContext(ctx context.Context) bool {\n    return ghttp.RequestFromCtx(ctx) != nil\n}","tryCatchPattern":"if err := o.Chat(prompt, isText); err != nil {\n    if err.Error() == \"request context is nil\" {\n        return fmt.Errorf(\"Chat must be called from a GoFrame HTTP handler: %w\", err)\n    }\n    return err\n}","preventionTips":["Always assign o.Ctx from the handler's request before calling Chat","Pass the original context into spawned goroutines, never context.Background()","Assert request availability in unit tests before exercising Chat","Avoid running Chat from background workers unless an event-writer abstraction is provided"],"tags":["go","goframe","context","http"],"backgroundTag":"missing-request-context","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}