apache/rocketmq · error · MQClientException

The producer service state not OK, maybe started once

Error message

The producer service state not OK, maybe started once, {serviceState}

What it means

MQClientException thrown from DefaultMQProducerImpl.start() when the service state is anything other than CREATE_JUST at switch entry - i.e. RUNNING (already started), START_FAILED, or SHUTDOWN_ALREADY. The producer is a one-shot state machine: CREATE_JUST -> RUNNING -> SHUTDOWN_ALREADY, and start() is only legal in the initial state. The message includes the offending state so you can tell which case you hit.

Solutions

  1. Audit call sites and guarantee start() runs exactly once per producer instance (guard with a started flag or use Spring lifecycle single-start)
  2. If the previous start failed, discard the instance and build a fresh DefaultMQProducer (state START_FAILED cannot be recovered)
  3. If you need to restart after shutdown(), create a new producer object; SHUTDOWN_ALREADY is terminal
  4. Catch this specific MQClientException in wrappers and treat it as 'already running' (idempotent start) rather than propagating

Example fix

// before
public void ensureStarted() { producer.start(); } // throws on 2nd call
// after
private final AtomicBoolean started = new AtomicBoolean();
public void ensureStarted() throws MQClientException {
    if (started.compareAndSet(false, true)) producer.start();
}
Defensive patterns

Strategy: validation

Validate before calling

 private final AtomicBoolean started = new AtomicBoolean();
public void start() throws MQClientException {
    if (started.compareAndSet(false, true)) {
        producer.start();
    } // else: already started, no-op
}

Try / catch

try {
    producer.start();
} catch (MQClientException e) {
    String state = e.getMessage(); // message embeds RUNNING / SHUTDOWN_ALREADY / START_FAILED
    if (state.contains("RUNNING")) { /* idempotent start - ignore */ }
    else if (state.contains("SHUTDOWN_ALREADY") || state.contains("START_FAILED")) {
        producer = newProducer(); producer.start(); // fresh instance
    } else throw e;
}

Prevention

When it happens

Trigger: Calling producer.start() twice without shutdown() in between; calling start() on a producer whose previous start() failed midway (state left as START_FAILED); calling start() after shutdown() (state SHUTDOWN_ALREADY is terminal).

Common situations: commonSituations

Related errors


AI-assisted analysis of apache/rocketmq@293f588571 (2026-08-14). Data as JSON: /api/errors/f7f4b5bde3577f53. Report an issue: GitHub.

Appendix: source

Thrown at client/src/main/java/org/apache/rocketmq/client/impl/producer/DefaultMQProducerImpl.java:281

                        null);
                }

                if (startFactory) {
                    mQClientFactory.start();
                }

                this.initTopicRoute();

                this.mqFaultStrategy.startDetector();

                log.info("the producer [{}] start OK. sendMessageWithVIPChannel={}", this.defaultMQProducer.getProducerGroup(),
                    this.defaultMQProducer.isSendMessageWithVIPChannel());
                this.serviceState = ServiceState.RUNNING;
                break;
            case RUNNING:
            case START_FAILED:
            case SHUTDOWN_ALREADY:
                throw new MQClientException("The producer service state not OK, maybe started once, "
                    + this.serviceState
                    + FAQUrl.suggestTodo(FAQUrl.CLIENT_SERVICE_NOT_OK),
                    null);
            default:
                break;
        }

        this.mQClientFactory.sendHeartbeatToAllBrokerWithLock();

        RequestFutureHolder.getInstance().startScheduledTask(this);

    }

    private void checkConfig() throws MQClientException {
        Validators.checkGroup(this.defaultMQProducer.getProducerGroup());

        if (this.defaultMQProducer.getProducerGroup().equals(MixAll.DEFAULT_PRODUCER_GROUP)) {
            throw new MQClientException("producerGroup can not equal " + MixAll.DEFAULT_PRODUCER_GROUP + ", please specify another one.",

View on GitHub (pinned to 293f588571)