{"record":{"id":"e0d2a771d5b9978f","repo":"MHSanaei/3x-ui","slug":"traffic-writer-stopped-before-write-completed","errorCode":null,"errorMessage":"traffic writer stopped before write completed","messagePattern":"traffic writer stopped before write completed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/web/service/traffic_writer.go","lineNumber":179,"sourceCode":"\ttimer := time.NewTimer(trafficWriterSubmitTimeout)\n\tdefer timer.Stop()\n\tselect {\n\tcase queue <- req:\n\t\ttwMu.Unlock()\n\tcase <-timer.C:\n\t\ttwMu.Unlock()\n\t\treturn errors.New(\"traffic writer queue full\")\n\t}\n\n\tselect {\n\tcase err := <-req.done:\n\t\treturn err\n\tcase <-done:\n\t\tselect {\n\t\tcase err := <-req.done:\n\t\t\treturn err\n\t\tdefault:\n\t\t\treturn errors.New(\"traffic writer stopped before write completed\")\n\t\t}\n\t}\n}\n","sourceCodeStart":161,"sourceCodeEnd":183,"githubUrl":"https://github.com/MHSanaei/3x-ui/blob/ad32144c42455696ea9f14e12168beac3e25f5d2/internal/web/service/traffic_writer.go#L161-L183","documentation":"Returned when the traffic writer's shutdown path (done channel closed) wins the race against a queued write request's own done channel. StopTrafficWriter cancels the writer context and drains the queue, but a request submitted concurrently with shutdown can observe the writer stopped before its result arrives. It indicates a write of unknown outcome: the transaction may or may not have committed.","triggerScenarios":"Calling a service method that routes through runSerializedTx (client/inbound mutations, AddTraffic callers) while StopTrafficWriter runs — typically during panel shutdown, SIGHUP-driven restart, or test teardown that calls Stop without quiescing producers first.","commonSituations":"Panel restart under active traffic polling; a cron job or in-flight HTTP request submitting a write exactly as the process re-executes itself; unit tests that stop the writer while goroutines from a prior request are still submitting.","solutions":["Treat as transient during shutdown: log and drop, or re-issue the operation after the panel/writer restarts.","Ensure producers stop before StopTrafficWriter: shut down HTTP server and cron jobs first, then stop the writer.","If seen outside a restart, check for code that calls StopTrafficWriter at runtime (only shutdown paths should)."],"exampleFix":"if err := submitTrafficWrite(fn); err != nil {\n    if strings.Contains(err.Error(), \"traffic writer stopped\") {\n        // shutdown race during restart; retry after StartTrafficWriter\n        return retryAfterRestart(fn)\n    }\n    return err\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"err := svc.UpdateClient(...)\nif err != nil && strings.Contains(err.Error(), \"traffic writer stopped\") {\n    // shutdown race: re-issue once the writer has been restarted\n    StartTrafficWriter()\n    err = svc.UpdateClient(...)\n}\nreturn err","preventionTips":["Order shutdown: stop HTTP server and cron jobs before StopTrafficWriter so no producers remain.","Treat occurrence outside restarts as a bug — only lifecycle code should stop the writer."],"tags":["concurrency","shutdown","lifecycle","database"],"backgroundTag":null,"analyzedSha":"ad32144c42455696ea9f14e12168beac3e25f5d2","analyzedAt":"2026-08-15T11:13:23.905Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}