quarkusio/quarkus · error · RuntimeException

OpenShift Operator API is not available, please add the open

Error message

OpenShift Operator API is not available, please add the openshift-model-operator module to your classpath

What it means

This substitution in Quarkus's openshift-client extension replaces fabric8 OpenShiftClientImpl methods that require the OpenShift Operator model at native-image build time. When the openshift-model-operator module is not on the classpath, calling egressRouters() throws a RuntimeException instead of failing with a linkage error. It is a GraalVM native-image @TargetClass substitution guarding an optional dependency.

Source

Thrown at extensions/openshift-client/runtime/src/main/java/io/quarkus/it/openshift/client/runtime/graal/OperatorSubstitutions.java:28

import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation;
import io.fabric8.kubernetes.client.dsl.Resource;
import io.fabric8.openshift.api.model.operator.imageregistry.v1.Config;
import io.fabric8.openshift.api.model.operator.imageregistry.v1.ConfigList;
import io.fabric8.openshift.api.model.operator.network.v1.EgressRouter;
import io.fabric8.openshift.api.model.operator.network.v1.EgressRouterList;
import io.fabric8.openshift.api.model.operator.network.v1.OperatorPKI;
import io.fabric8.openshift.api.model.operator.network.v1.OperatorPKIList;
import io.fabric8.openshift.client.dsl.OpenShiftOperatorAPIGroupDSL;

/**
 * Allows the exclusion of the openshift-model-operator model without breaking the --link-at-build-time check.
 */
@TargetClass(className = "io.fabric8.openshift.client.impl.OpenShiftClientImpl", onlyWith = OperatorSubstitutions.NoOpenShiftOperatorModel.class)
public final class OperatorSubstitutions {

    @Substitute
    public MixedOperation<EgressRouter, EgressRouterList, Resource<EgressRouter>> egressRouters() {
        throw new RuntimeException(OperatorSubstitutions.Constants.ERROR_MESSAGE);
    }

    @Substitute
    public NonNamespaceOperation<Config, ConfigList, Resource<Config>> imageRegistryOperatorConfigs() {
        throw new RuntimeException(OperatorSubstitutions.Constants.ERROR_MESSAGE);
    }

    @Substitute
    public OpenShiftOperatorAPIGroupDSL operator() {
        throw new RuntimeException(OperatorSubstitutions.Constants.ERROR_MESSAGE);
    }

    @Substitute
    public MixedOperation<OperatorPKI, OperatorPKIList, Resource<OperatorPKI>> operatorPKIs() {
        throw new RuntimeException(OperatorSubstitutions.Constants.ERROR_MESSAGE);
    }

    static final class Constants {

View on GitHub (pinned to e1c734241f)

Solutions

  1. Add io.quarkus:quarkus-openshift-client plus the fabric8 openshift-model-operator dependency (quarkus-openshift-client brings it transitively when the operator model flavor is used) to the project's pom.xml
  2. Verify the app is not depending on an old/bom-pinned fabric8 version that excludes openshift-model-operator
  3. If Operator APIs are not needed, avoid calling egressRouters()/operator() style methods

Example fix

// before
<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-openshift-client</artifactId>
</dependency>
// after
<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-openshift-client</artifactId>
</dependency>
<!-- operator model available transitively; if excluded, re-add -->
<dependency>
  <groupId>io.fabric8</groupId>
  <artifactId>openshift-model-operator</artifactId>
</dependency>
Defensive patterns

Strategy: validation

Validate before calling

// before calling egressRouters(), verify the model is on the classpath
static boolean operatorModelAvailable() {
    try {
        Class.forName("io.fabric8.openshift.client.dsl.OpenShiftOperatorAPIGroupDSL");
        return true;
    } catch (ClassNotFoundException e) {
        return false;
    }
}

Type guard

static boolean canUseOperatorApis(OpenShiftClient client) {
    return client instanceof OpenShiftClientImpl
        && operatorModelAvailable();
}

Try / catch

try {
    client.egressRouters().inAnyNamespace().list();
} catch (RuntimeException e) {
    if (e.getMessage() != null && e.getMessage().contains("openshift-model-operator")) {
        throw new IllegalStateException("Add io.fabric8:openshift-model-operator to the classpath", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling OpenShiftClient.egressRouters() in a native-image build (or build-time-substituted context) when io.fabric8:openshift-model-operator is absent from the classpath.

Common situations: Users of the Quarkus openshift-client extension who interact with OpenShift Operator resources (EgressRouter, OperatorPKI, image registry configs) but only added the base openshift-client dependency; the app works in JVM mode but fails at runtime in native mode.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/96fb031bc306febd. Report an issue: GitHub.