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

  1. Treat as transient during shutdown: log and drop, or re-issue the operation after the panel/writer restarts.
  2. Ensure producers stop before StopTrafficWriter: shut down HTTP server and cron jobs first, then stop the writer.
  3. 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

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


AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15). Data as JSON: /api/errors/e0d2a771d5b9978f. Report an issue: GitHub.