--- title: Troubleshoot an InferenceService description: Find out why an InferenceService isn't ready or serving by checking its status, model, runtime and logs, then look up the fix for what you see. since: v1.3 --- When an [InferenceService](../../concepts/serving/inference-services.md) fails to become ready, serve requests or pick up a change, check its status, model, runtime and logs. Then look up what you see in [Troubleshooting](#troubleshooting). The examples use the InferenceService `qwen2-5-7b` in the namespace `demo`. It serves the ClusterBaseModel `qwen2-5-7b` with a runtime that OME selected, and its engine runs on [OMENative](../../concepts/omenative/overview.md) as the InferenceReplica `qwen2-5-7b-engine` and its [Instances](../../concepts/omenative/instances.md). OME runs in the namespace `ome`.
- The kubectl-ome plugin, and a kubeconfig for the cluster. See [kubectl-ome overview and install](../../reference/kubectl-ome/overview.md). - Read access to the InferenceService, its pods, pod logs, events and InferenceReplicas; to base models and serving runtimes, cluster-scoped ones included; and to pod logs in OME's namespace. - If OME runs in another namespace, use it wherever a command names `ome`, and pass it to `kubectl ome status` and `kubectl ome instance status` with `--ome-namespace`.
## Symptoms and where to start Unless kubectl rejected the InferenceService, start with [Step 1](#step-1-get-the-overall-status), whose report often points at the cause. Then find what you see: | Symptom | Where to look | | --- | --- | | The admission webhook denied the request | [The webhook rejects the InferenceService](#the-webhook-rejects-the-inferenceservice) | | The InferenceService has no pods | [Step 2](#step-2-check-the-model), [Step 3](#step-3-check-the-runtime), then [The InferenceService gets no pods](#the-inferenceservice-gets-no-pods) | | Pods stay `Pending` | [Pods stay Pending](#pods-stay-pending) | | Pods crash or keep restarting | [Step 4](#step-4-read-the-logs) | | Pods run, but the InferenceService doesn't become Ready | [Pods run, but the InferenceService isn't Ready](#pods-run-but-the-inferenceservice-isnt-ready) | | An InferenceService that uses fine-tuned weights doesn't become Ready | [When the InferenceService doesn't become ready](../../concepts/models/fine-tuned-weights.md#when-the-inferenceservice-doesnt-become-ready), on the Fine-tuned weights page | | A change doesn't roll out | [A change doesn't roll out](#step-4-check-the-rollout) | | The InferenceService runs too many or too few pods | [The number of pods is wrong](#the-number-of-pods-is-wrong) | | The InferenceService has no URL | [The InferenceService has no URL](#the-inferenceservice-has-no-url) | | Requests fail or don't arrive | [Requests fail](#requests-fail) | | Some pods of an Instance run while others stay `Pending` | [Part of an Instance stays Pending](#part-of-an-instance-stays-pending) | | An Instance is `Failed`, or never becomes ready | [Instances fail or never become ready](#instances-fail-or-never-become-ready) | | Pods stay `Terminating` after you delete the InferenceService | [Pods stay Terminating after a delete](#pods-stay-terminating-after-a-delete) | ## Step 1: Get the overall status `kubectl ome status` reports on the InferenceService, its pods and their Warning events. Just after you create the InferenceService, the report looks like this: ```bash kubectl ome status qwen2-5-7b -n demo ``` ```output FIELD VALUE Name qwen2-5-7b Namespace demo Ready NotRecorded / Unavailable Ready reason Declared runtime Model qwen2-5-7b Generation 1 observed=0; advisory Unverifiable Pod observation Reported count=0 truncated=false Event observation Reported count=0 truncated=false engine NotRecorded / Unavailable; Ready pods=0/0 restarts=0 Rollout Unknown reported=Unknown Rollout evidence Reported / Unverifiable Autoscaling Unavailable / Unavailable parent status Placement NotConfigured / NotApplicable / NotRecorded Traffic Unavailable / Unavailable parent status Runtime active Unavailable / Unavailable AutoSelectionNotProbed Accelerator Unavailable / Unavailable AutoSelectionNotProbed Full safe values Use -o json or -o yaml Rollout detail kubectl ome rollout status NAME Autoscale detail kubectl ome autoscale status NAME Placement detail kubectl ome placement status NAME Traffic detail kubectl ome traffic status NAME Runtime detail kubectl ome runtime effective NAME Accelerator detail kubectl ome accelerator explain NAME ``` In a healthy report, `Ready` and each component row start with `True / Valid`, all pods are ready, and no `Issue` row appears. Otherwise, read these rows: | Row | What it tells you | | --- | --- | | `Ready`, `Ready reason` | The InferenceService's `Ready` condition and its reason. If it stays `NotRecorded / Unavailable`, see [The InferenceService gets no pods](#the-inferenceservice-gets-no-pods). | | `engine`, `decoder`, `router` | A component's `Ready` condition, ready and total pods, and container restarts. For restarts, see [Step 4](#step-4-read-the-logs). | | `Runtime active`, `Accelerator` | Whether OME reports an active runtime and accelerator. `AutoSelectionNotProbed` means that OME selects the runtime and the report doesn't work out which one. [Step 3](#step-3-check-the-runtime) does. | | `Warning Inference...`, `Warning Pod` | A recent Warning event on the InferenceService or a pod, and its reason. It can remain after a recovery. | | `Issue` | Evidence that the report couldn't collect or trust. | Add `-o wide` for the condition and event messages. The report leaves out Normal events and events on InferenceReplicas: list those with `kubectl get events`. [kubectl ome status](../../reference/kubectl-ome/status.md#output-fields) describes every row. ## Step 2: Check the model Steps 2 and 3 apply when the InferenceService names a base model in `spec.model`. An InferenceService without one names its runtime in `spec.runtime`, and OME skips the model fetch and runtime selection: go to [Step 4](#step-4-read-the-logs). Unless the model's weights come from a PVC, OME schedules the pods only onto nodes that hold the weights. Check the model's state and which nodes have it: ```bash kubectl get clusterbasemodel qwen2-5-7b \ -o 'custom-columns=STATE:.status.state,READY:.status.nodesReady[*],FAILED:.status.nodesFailed[*]' ``` ```output STATE READY FAILED Ready gpu-node-1,gpu-node-2 ``` For a BaseModel, run `kubectl get basemodel qwen2-5-7b -n demo` with the same columns. Then act on `STATE`: - `Ready`: at least one node has the model. Other nodes may still be downloading it, so some pods can stay `Pending`. - `In_Transit`: no node has the model yet. The model agent downloads it, and the `ome-resources` chart installs the agent only when you set `modelAgent.enabled: true`. See [Run the model agent](../operate-ome/model-agent.md). - `Failed`: no node has the model. `FAILED` lists the nodes whose download failed. See [Model lifecycle](../../concepts/models/base-models.md#model-lifecycle). A `pvc://` model needs no agent: a metadata Job sets its state. See [Serve models from a PVC](../deploy-models/serve-models-from-pvc.md#troubleshooting). ## Step 3: Check the runtime [`kubectl ome runtime explain`](../../reference/kubectl-ome/runtime.md#explain) runs OME's runtime selection for the model and lists every runtime it considered: ```bash kubectl ome runtime explain --isvc qwen2-5-7b -n demo ``` The first row with `Yes` in `COMPATIBLE` is the runtime that OME selects, and each `No` row gives its reason in `REASON`. When every row says `No`, OME leaves the serving workload as it is: read the reasons as [Troubleshoot runtime selection](../deploy-models/troubleshoot-runtime-selection.md#step-3-read-the-exclusion-reasons) explains. The command finds the model whatever `spec.model.kind` says, so a `Yes` row doesn't prove that the kind is right. See [An InferenceService that serves a BaseModel gets no pods](#an-inferenceservice-that-serves-a-basemodel-gets-no-pods). ## Step 4: Read the logs [`kubectl ome logs`](../../reference/kubectl-ome/logs.md) reads the main container, `ome-container`, of the InferenceService's pods. Read the engine's logs from the last 10 minutes: ```bash kubectl ome logs qwen2-5-7b -n demo --component engine --since 10m ``` If the command fails with `no pods found for InferenceService "qwen2-5-7b" in namespace "demo"`, check the name and the namespace, then see [The InferenceService gets no pods](#the-inferenceservice-gets-no-pods). For a restarted pod's previous run, use kubectl. List the engine's pods: ```bash kubectl get pods -n demo -l ome.io/inferenceservice=qwen2-5-7b,component=engine ``` On OMENative, a pod's name includes its Instance, as in `qwen2-5-7b-engine-0-default-0` for Instance `0`. A Deployment's pods have names like `qwen2-5-7b-engine-6c9f8d7b5-x2kqp`. Read the previous run of `ome-container`: ```bash kubectl logs qwen2-5-7b-engine-0-default-0 -n demo -c ome-container --previous ``` The log usually ends with the error that stopped the container. When a pod of a multi-pod Instance fails, OME recreates all the Instance's pods by default, and their logs are gone: see [Instance restart policy](../../concepts/omenative/instance-restart-policy.md). OME still records the Instance's last failure: `kubectl ome instance status qwen2-5-7b 0 --component engine -n demo -o wide` shows its pod, container, reason and exit code. Some failures show only in the controller's logs. Read every replica, because only the leader acts, and pass `--tail=-1`, because a label selector limits kubectl to the last 10 lines per pod: ```bash kubectl logs -n ome -l control-plane=ome-controller-manager --tail=-1 | grep qwen2-5-7b ``` ## Troubleshooting ### The webhook rejects the InferenceService kubectl prints `admission webhook "inferenceservice.ome-webhook-server.validator" denied the request:` and a message. The webhook checks every create and update, so it can reject an InferenceService that it admitted before, even for a label change. Find the first row that matches: | The message includes | Cause and fix | | --- | --- | | `at least one of spec.model or spec.runtime must be set` | Set `spec.model`, or name the runtime in `spec.runtime`. | | `referenced model "qwen2-5-7b" not found in namespace "demo"` | Check `spec.model.name`, or create the model as a BaseModel in `demo` or a ClusterBaseModel. | | `is disabled` | The named model or runtime sets `spec.disabled: true`. Set it to `false`, or use another. | | `model format name is required` | OME hasn't recorded the model's format. See [The message says the model format name is required](../deploy-models/troubleshoot-runtime-selection.md#the-message-says-the-model-format-name-is-required). | | `no model cache provider is configured for sharded model loading` | The model sets `distribution: Sharded`, which can't be served in this release. See [Distribution](../../concepts/models/base-models.md#distribution). | | `srt-qwen2-5-7b not found in namespace demo` | The runtime that the InferenceService names is missing. See [The runtime you named isn't found](../deploy-models/troubleshoot-runtime-selection.md#the-runtime-you-named-isnt-found). | | `no supporting runtime found for model qwen2-5-7b` | No runtime supports the model. See [Troubleshoot runtime selection](../deploy-models/troubleshoot-runtime-selection.md). | The warning `Runtime srt-qwen2-5-7b will be auto-selected for model qwen2-5-7b` is expected. A warning that includes `does not declare support for model` means that the runtime you named doesn't list the model's format: see [kubectl prints a `does not declare support` warning](../deploy-models/reference-a-runtime-explicitly.md#what-ome-checks-when-you-apply). ### The InferenceService gets no pods `kubectl ome status` shows `Pod observation` as `Reported count=0 truncated=false`. Look for these causes in order, and fix the first that applies. The first three apply when the InferenceService names a base model. 1. `spec.model.kind` differs from the model's kind. See the next section. 2. A `ModelReconcileError` Warning event, such as `specified base model qwen2-5-7b is disabled`. Fix the model that it names. 3. A Normal `ModelNotReady` event, which the status report leaves out: the model is Sharded, which can't be served in this release. List these events with `kubectl get events -n demo --field-selector reason=ModelNotReady`. See [Distribution](../../concepts/models/base-models.md#distribution). 4. A `RuntimeNotFound` Warning event: no runtime supports the model. See [Troubleshoot runtime selection](../deploy-models/troubleshoot-runtime-selection.md). An InferenceService that was already serving keeps its pods. 5. An `AcceleratorClassError` Warning event, or the `BestFit` accelerator policy. See [The InferenceService has an AcceleratorClassError event](../deploy-models/select-accelerators.md#the-inferenceservice-has-an-acceleratorclasserror-event) and [BestFit doesn't create or update the workload](../deploy-models/select-accelerators.md#bestfit-doesnt-create-or-update-the-workload). 6. The InferenceService lacks an `engine` section, so OME creates no engine. Add one: a section that sets only `minReplicas` is enough. Otherwise, read the controller's logs, as in [Step 4](#step-4-read-the-logs). ### An InferenceService that serves a BaseModel gets no pods `spec.model.kind` defaults to `ClusterBaseModel`. Suppose `qwen2-5-7b` is a BaseModel in `demo`, and the InferenceService leaves out the kind. The webhook admits it, but OME leaves the serving workload as it is and records no event or condition. The status report shows `Ready` as `NotRecorded / Unavailable`, and the controller's logs show `Failed to get referenced ClusterBaseModel`. The reverse, `kind: BaseModel` for a ClusterBaseModel, logs `Failed to get referenced BaseModel`. Check the kind: ```bash kubectl get inferenceservice qwen2-5-7b -n demo -o custom-columns=KIND:.spec.model.kind,MODEL:.spec.model.name ``` ```output KIND MODEL ClusterBaseModel qwen2-5-7b ``` Set the kind to the model's kind, here `BaseModel`: ```bash kubectl patch inferenceservice qwen2-5-7b -n demo --type=merge -p '{"spec":{"model":{"kind":"BaseModel"}}}' ``` ```output inferenceservice.ome.io/qwen2-5-7b patched ``` ### Pods stay Pending List the scheduler's events in the namespace: ```bash kubectl get events -n demo --field-selector reason=FailedScheduling ``` Their messages point at one of these causes: - The InferenceService names a base model, and not enough nodes have it. Unless the model is served from a PVC, OME schedules the pods only onto nodes labeled `models.ome.io/clusterbasemodel.qwen2-5-7b=Ready`, or `models.ome.io/demo.basemodel.qwen2-5-7b=Ready` for a BaseModel. List them: ```bash kubectl get nodes -l models.ome.io/clusterbasemodel.qwen2-5-7b=Ready -o name ``` ```output node/gpu-node-1 node/gpu-node-2 ``` An empty list means that no node has the model yet: go back to [Step 2](#step-2-check-the-model). - The nodes lack free GPUs, CPU or memory for the pod's requests. - The pods of a multi-pod Instance wait to be scheduled together: see [Gang scheduling](../../concepts/serving/gang-scheduling.md). On OMENative, pods that stay unschedulable for too long fail their Instance: see [Instances fail or never become ready](#instances-fail-or-never-become-ready). ### Pods run, but the InferenceService isn't Ready RawDeployment and `MultiNode` (deprecated) components need the Prometheus Operator's PodMonitor CRD. RawDeployment is the default for a component without a leader and workers. If the CRD is missing, their pods run, but the InferenceService never becomes Ready. OME records no event about it. Check for the CRD: ```bash kubectl get crd podmonitors.monitoring.coreos.com ``` ```output Error from server (NotFound): customresourcedefinitions.apiextensions.k8s.io "podmonitors.monitoring.coreos.com" not found ``` Install it as in [The PodMonitor CRD](../../getting-started/install.md#the-podmonitor-crd), then restart the controller, which looks for the CRD only when it starts: ```bash kubectl rollout restart deployment/ome-controller-manager -n ome ``` ```output deployment.apps/ome-controller-manager restarted ``` ### A change doesn't roll out {#step-4-check-the-rollout} `kubectl ome rollout explain` shows the rollout plan and what holds it: ```bash kubectl ome rollout explain qwen2-5-7b -n demo ``` Read the `Effective` view, the plan that OME follows: | Row | Healthy | Otherwise | | --- | --- | --- | | `READY` | Starts with `True/Pinned` or `True/NoActiveRun`. | `False` means that the rollout is parked, and the old revision keeps serving. The reason says why. | | `DRIFT` | Starts with `False/InSync`. | `True/SpecNewerThanRun` or `True/PolicyNewerThanRun` means that your edit waits for the next rollout run. See [Repin a drifted rollout plan](../roll-out-changes/repin-a-drifted-rollout-plan.md). | | `HOLD` | No row. | Each row names a hold. `GlobalPause` means that the rollout is paused: see [Pause and resume a rollout](../roll-out-changes/pause-and-resume-a-rollout.md). | [kubectl ome rollout explain](../../reference/kubectl-ome/rollout.md#explain-output-fields) lists the other rows. Two other causes appear outside the plan: - With `spec.runtime.autoSync: false`, the InferenceService pins its runtime, so runtime changes don't reach it. See [Runtime revisions and pinning](../../concepts/runtimes/runtime-revisions.md). - A `RetryHeld` Warning event means that OME stopped retrying a failed update of an OMENative component. See [Release a held revision](../roll-out-changes/release-a-held-revision.md). ### The number of pods is wrong `kubectl ome autoscale status` shows the autoscaling that the controller reports for each component: ```bash kubectl ome autoscale status qwen2-5-7b -n demo ``` In a component's column, `CLASS` names the autoscaler, `TARGET-NAME` the object it scales, and `CURRENT` and `DESIRED` its replica counts. Add `--live-scaler` to also read the HorizontalPodAutoscaler or KEDA ScaledObject. [Component autoscaling](../../concepts/serving/component-autoscaling.md) explains where the settings come from, and [kubectl ome autoscale](../../reference/kubectl-ome/autoscale.md) describes every row. ### The InferenceService has no URL If the InferenceService also lacks pods, start with [The InferenceService gets no pods](#the-inferenceservice-gets-no-pods). If its pods run, see [Pods run, but the InferenceService isn't Ready](#pods-run-but-the-inferenceservice-isnt-ready): a missing PodMonitor CRD also keeps OME from setting the URL. The `ome-resources` chart turns off ingress creation by default. OME then points the URL at a Service named after the InferenceService, as in `http://qwen2-5-7b.demo.svc.cluster.local:8080`, and the `IngressReady` condition says so: ```bash kubectl get inferenceservice qwen2-5-7b -n demo -o jsonpath='{.status.conditions[?(@.type=="IngressReady")].reason}' ``` ```output IngressDisabled ``` OME skips the Service and the URL for an InferenceService labeled `networking.knative.dev/visibility: cluster-local`, and for one without an engine or a router. To reach the service from outside the cluster, see [Expose a service without ingress](../networking/expose-without-ingress.md) or [Configure ingress](../networking/configure-ingress.md). ### Requests fail Send the InferenceService a test request, as in [Step 7 of Serve your first model](../../getting-started/serve-your-first-model.md#step-7-send-a-request). If it fails, read the engine's logs, as in [Step 4](#step-4-read-the-logs). When the InferenceService sets a traffic policy, a traffic annotation or a canary, `kubectl ome traffic explain` compares it with what the controller reports. Suppose `qwen2-5-7b` is exposed through Envoy Gateway and sets `spec.traffic.algorithm: RoundRobin`: ```bash kubectl ome traffic explain qwen2-5-7b -n demo ``` ```output LAYER STATE VALUE SOURCE SUMMARY Consistent - Computed/Current INTENT Declared RoundRobin Declared/Current SUPPORT Honored - Computed/Current TRANSLATE Computed envoy-gateway Computed/Current REALIZE Reported r=1 e=0 w=0 Reported/Current CHECK Match algorithm Computed/Current CHECK Match policy Computed/Current CHECK NotApplicable canary-weight Computed/Current ``` `SUMMARY` is `Consistent` when the declared and reported policy agree, and [kubectl ome traffic](../../reference/kubectl-ome/traffic.md#explain-output-fields) explains `Mismatch` and `Unsupported`. The command reads only the InferenceService, so `Consistent` doesn't prove that requests reach the pods. See [Traffic policy](../../concepts/rollouts-and-traffic/traffic-policy.md). ### Part of an Instance stays Pending OME gang-schedules a multi-pod Instance through a PodGroup, which needs the scheduler-plugins PodGroup CRD. If the CRD is missing, OME creates the pods anyway, so the scheduler can place some of them, and sets `GangSchedulingUnavailable` to `True` on the component. Check its reason: ```bash kubectl get inferenceservice qwen2-5-7b -n demo -o jsonpath='{.status.components.engine.lifecycle.conditions[?(@.type=="GangSchedulingUnavailable")].reason}' ``` ```output PodGroupCRDNotInstalled ``` Install the CRD and restart the controller, as [Install the PodGroup CRD](../../concepts/serving/gang-scheduling.md#when-the-podgroup-crd-is-missing) shows. With the CRD in place, a `MaybeNoGangScheduler` Warning event means that the pods use the default scheduler, which ignores PodGroups unless you added the Coscheduling plugin. See [Bring a gang-aware scheduler](../../concepts/serving/gang-scheduling.md#bring-a-gang-aware-scheduler). ### Instances fail or never become ready When OME moves an Instance to the `Failed` phase, it records an `InstanceFailed` Warning event on the InferenceService. List these events: ```bash kubectl get events -n demo --field-selector reason=InstanceFailed ``` The message names the Instance and its pod, and ends with one of these causes: | The cause starts with | What happened | What to do | | --- | --- | --- | | A kubelet reason, such as `CrashLoopBackOff` or `ImagePullBackOff` | A container stayed waiting for longer than the stuck-pod grace period, `60s` with the `ome-resources` chart. | Read the logs, as in [Step 4](#step-4-read-the-logs). | | `Unschedulable:` | The pods stayed unschedulable for longer than the unschedulable grace period, `15m` with the chart. | See [Pods stay Pending](#pods-stay-pending). | | `DeadlineExceeded:` | The Instance wasn't ready by its readiness deadline, `30m` by default. Waiting for the scheduler, a scheduling gate or quota doesn't count. | Read the logs. If the model needs longer to load, raise the deadline: see [Set Instance readiness deadlines](../omenative/set-instance-readiness-deadlines.md). | To find the failed Instances and read their last failures, follow [Step 1 of Reset failed Instances](../omenative/reset-failed-instances.md#step-1-find-the-parked-instances). Fix the cause. A fix to the pod template, such as a new image, rolls out to every Instance, failed ones included. After any other fix, [release the held revision](../roll-out-changes/release-a-held-revision.md) if the InferenceService has a `RetryHeld` Warning event, or else [reset the failed Instances](../omenative/reset-failed-instances.md). When an Instance stays unready but never fails, check the engine's deadline: ```bash kubectl get inferenceservice qwen2-5-7b -n demo -o jsonpath='{.status.components.engine.lifecycle.conditions[?(@.type=="InstanceReadyTimeoutUnconfigured")].message}' ``` ```output Instance readiness deadline is 30m0s ``` If the message starts with `no instanceReadyTimeout is set`, OME waits for you instead of failing the Instance. [The Instance is stuck and never fails](../omenative/set-instance-readiness-deadlines.md#the-instance-is-stuck-and-never-fails) covers this and the other causes. ### Pods stay Terminating after a delete Deleting an InferenceService deletes its InferenceReplicas, and each keeps the finalizer `ome.io/ir-teardown` until its pods and PodGroups are gone. A pod on a node that stopped responding holds that up. OME records `TeardownBlocked` or `TeardownDeadlineExceeded` events on the InferenceReplica, not the InferenceService: ```bash kubectl get events -n demo --field-selector involvedObject.kind=InferenceReplica,involvedObject.name=qwen2-5-7b-engine ``` Both messages end with a `kubectl patch` command that removes the finalizer. Read [Recover stuck deletions](../omenative/recover-stuck-deletions.md) before you run it. ## Next steps - [kubectl ome status](../../reference/kubectl-ome/status.md): every row of the status report. - [Troubleshoot runtime selection](../deploy-models/troubleshoot-runtime-selection.md): when no runtime matches the model. - [Observe OMENative in status](../../concepts/architecture/deployment-modes.md#observe-omenative-in-status): the status that OMENative components report.