conductor-oss/conductor · error · RuntimeException

Exchange type is undefined

Error message

Exchange type is undefined

What it means

Thrown as RuntimeException by getOrCreateExchange when the 'type' argument (the AMQP exchange type: direct/fanout/topic/headers) is empty. The value comes from settings.getExchangeType(). RabbitMQ requires an exchange type at declare time, so an empty value cannot proceed.

Source

Thrown at amqp/src/main/java/com/netflix/conductor/contribs/queue/amqp/AMQPObservableQueue.java:561

                settings.getExchangeType(),
                settings.isDurable(),
                settings.autoDelete(),
                settings.getArguments());
    }

    private AMQP.Exchange.DeclareOk getOrCreateExchange(
            ConnectionType connectionType,
            String name,
            final String type,
            final boolean isDurable,
            final boolean autoDelete,
            final Map<String, Object> arguments)
            throws Exception {
        if (StringUtils.isEmpty(name)) {
            throw new RuntimeException("Exchange name is undefined");
        }
        if (StringUtils.isEmpty(type)) {
            throw new RuntimeException("Exchange type is undefined");
        }
        Channel chn = null;
        try {
            LOGGER.debug("Creating exchange {} of type {}", name, type);
            chn =
                    amqpConnection.getOrCreateChannel(
                            connectionType, getSettings().getQueueOrExchangeName());
            return chn.exchangeDeclare(name, type, isDurable, autoDelete, arguments);
        } catch (final Exception e) {
            LOGGER.warn("Failed to create exchange {} of type {}", name, type, e);
            throw e;
        } finally {
            if (chn != null) {
                try {
                    amqpConnection.returnChannel(connectionType, chn);
                } catch (Exception e) {
                    LOGGER.error("Failed to return the channel of {}. {}", connectionType, e);
                }

View on GitHub (pinned to cf7c3e4a8a)

Solutions

  1. Add exchangeType=topic (or direct/fanout/headers) to the amqp_exchange URI.
  2. Set a non-empty default conductor.workflow.event-queues.amqp.exchangeType if exchanges are used broadly.
  3. Validate the resolved settings.getExchangeType() before calling getOrCreateExchange.

Example fix

// before
amqp_exchange:myExchange?durable=true
// after
amqp_exchange:myExchange?exchangeType=topic&durable=true
Defensive patterns

Strategy: validation

Validate before calling

import org.apache.commons.lang3.StringUtils;
import java.util.Set;
String exchangeType = settings.getExchangeType();
Set<String> valid = Set.of("direct", "fanout", "topic", "headers");
if (useExchange && (StringUtils.isBlank(exchangeType) || !valid.contains(exchangeType.toLowerCase()))) {
    throw new IllegalStateException(
        "AMQP exchangeType must be one of " + valid + "; got: " + exchangeType);
}

Prevention

When it happens

Trigger: An exchange-typed AMQP URI with no exchangeType query parameter and no default exchangeType configured in AMQPEventQueueProperties. getOrCreateExchange(connectionType) passes settings.getExchangeType() which resolves to empty.

Common situations: Forgetting the exchangeType= query param in the URI when the global default is unset; setting conductor.workflow.event-queues.amqp.exchangeType to blank; upgrading config where the property key changed.

Related errors


AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14). Data as JSON: /api/errors/bdb5c11c93730d69. Report an issue: GitHub.