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
- 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
- Verify the app is not depending on an old/bom-pinned fabric8 version that excludes openshift-model-operator
- 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
- Always add io.fabric8:openshift-model-operator when using Operator/OperatorPKI/EgressRouter APIs
- Test the native build early — these substitutions only fail in native mode
- Import the Quarkus BOM so fabric8 modules stay version-aligned
- Grep the codebase for OpenShift Operator APIs before removing 'unused' dependencies
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
- OpenShift Miscellaneous API is not available, please add the
- Could not read class path resources having path '${resourceP
- Expected : after attribute
- Failed to load CodeGenProvider class from deployment classlo
- Failed to read %s
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/96fb031bc306febd.
Report an issue: GitHub.