MHSanaei/3x-ui · warning
traffic writer stopped before write completed
Error message
traffic writer stopped before write completed
What it means
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.
Source
Thrown at internal/web/service/traffic_writer.go:179
timer := time.NewTimer(trafficWriterSubmitTimeout)
defer timer.Stop()
select {
case queue <- req:
twMu.Unlock()
case <-timer.C:
twMu.Unlock()
return errors.New("traffic writer queue full")
}
select {
case err := <-req.done:
return err
case <-done:
select {
case err := <-req.done:
return err
default:
return errors.New("traffic writer stopped before write completed")
}
}
}
View on GitHub (pinned to ad32144c42)
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).
Example fix
if err := submitTrafficWrite(fn); err != nil {
if strings.Contains(err.Error(), "traffic writer stopped") {
// shutdown race during restart; retry after StartTrafficWriter
return retryAfterRestart(fn)
}
return err
} Defensive patterns
Strategy: retry
Try / catch
err := svc.UpdateClient(...)
if err != nil && strings.Contains(err.Error(), "traffic writer stopped") {
// shutdown race: re-issue once the writer has been restarted
StartTrafficWriter()
err = svc.UpdateClient(...)
}
return err Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- traffic writer queue full
- xray is already running
- xray is not running
- XUI_DB_TYPE=postgres but XUI_DB_DSN is empty
- sqlite backup is unavailable for PostgreSQL
AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15).
Data as JSON: /api/errors/e0d2a771d5b9978f.
Report an issue: GitHub.