Azure OpenAI model deployment not found — deployment deleted, wrong subscription, or endpoint URL wrong (2026) — Fix Guide (2026) | AI Error Hub
Home Providers Azure OpenAI Deployment truly missing
Azure OpenAI Routing · Deployment Severity: High HTTP 404

Azure OpenAI DeploymentNotFound — deployment truly missing or unreachable

The naming-confusion version of this error is covered elsewhere. This page is for when the deployment name is right, the SDK client is right, and you still get 404 — because the deployment does not exist where you think it does.

By Ahmed R. · Senior AI Infrastructure Engineer Published 2026-07-26 Verified 2026-07-26

Quick fix (TL;DR)

Resolution: When you have ruled out the deployment-name-vs-model-id confusion, DeploymentNotFound means the deployment does not exist at the URL you're calling. Root causes are (a) it was deleted (accidentally or via Terraform), (b) the endpoint URL points to a different resource, (c) the deployment lives in a different subscription than you're authenticated against, or (d) it was renamed and code still references the old name. Fix with the diagnostic script — it walks all four causes in 20 seconds.

Real error messages you'll see

These are the exact strings returned by the Azure OpenAI service and its SDKs when this error occurs. Copy-paste-searching any of them should land on this page.

Python SDK — 404 with correct-looking config
openai.NotFoundError: Error code: 404 - {'error': {'code': 'DeploymentNotFound', 'message': 'The API deployment for this resource does not exist. If you created the deployment within the last 5 minutes, please wait a moment and try again.'}}
Portal state disagreeing with API
# In portal → your resource → Deployments
# gpt4o-prod    | gpt-4o    | 2024-08-06    | Active    | GlobalStandard
#
# But API call:
curl "https://myresource.openai.azure.com/openai/deployments/gpt4o-prod/chat/completions?api-version=2024-10-21" \
  -H "api-key: $KEY" -d '{"messages":[{"role":"user","content":"hi"}]}'
# {"error":{"code":"DeploymentNotFound",...}}

Reference

Nine reasons a "correct" deployment name still 404s

#CauseHow to confirm
1Deployment was deletedPortal → Deployments; if absent, deleted
2Endpoint URL points to a different resourceParse hostname; verify resource
3Wrong subscription context in authCompare token oid with subscription's tenant
4Terraform apply recreated with new nameCheck Terraform state history
5Deployment renamed in a fix, code not updatedCheck ARM audit log
6Case mismatch — Azure is case-sensitive heregpt4o-prodgpt4o-Prod
7Deployment moved to different resourceCheck ARM audit log for move ops
8Resource itself deleted (accidental cleanup)az resource show returns 404
9Cross-tenant confusionToken tenant ≠ resource tenant

Root causes, ranked by frequency

Based on developer reports across Azure OpenAI SDK forums, GitHub issues, and Microsoft Q&A during 2025–2026.

  • 25%
    Deployment deleted. Accidental portal deletion, or Terraform destroy on the wrong workspace. Zero recovery — you must recreate.
  • 18%
    Wrong endpoint in config. Not the naming-vs-model-id case — the endpoint hostname itself points to a different resource than where the deployment lives.
  • 14%
    Terraform recreated with new name. Immutable resource replaced; new deployment has a slightly different name that code did not adopt.
  • 12%
    Deployment was renamed. An engineer renamed for consistency; the code did not catch up. ARM allows rename; SDK code depends on the exact string.
  • 10%
    Case mismatch. Deployment name is case-sensitive in the URL path.
  • 8%
    Wrong subscription context. Managed identity has role on Subscription A; endpoint is on Subscription B. Auth may or may not succeed depending on role scope; DeploymentNotFound is the frequent shape.
  • 6%
    Resource itself deleted. An automated cleanup script targeting "dev-*" caught a resource that got renamed to match.
  • 4%
    Cross-tenant setup misconfigured. Multi-tenant SaaS; user's token from one tenant, deployment in another. Fails 404.

Fixes — copy-paste solutions

Fix #1

Diagnostic script — walks all nine causes in order

The single script that pinpoints which cause you have.

Reads your config, resolves the resource, lists deployments, checks case-sensitivity, and prints a specific "this is the cause" verdict at the end. Run this before opening a support ticket.

diagnose_404.py
"""Diagnose a persistent DeploymentNotFound. Prints a verdict."""
import os
import sys
from urllib.parse import urlparse
from azure.identity import DefaultAzureCredential
from azure.mgmt.cognitiveservices import CognitiveServicesManagementClient
from azure.core.exceptions import ResourceNotFoundError

endpoint = os.environ["AZURE_OPENAI_ENDPOINT"]
deployment_name = os.environ["AZURE_OPENAI_DEPLOYMENT"]
subscription_id = os.environ["AZURE_SUBSCRIPTION_ID"]

# Parse resource name from endpoint URL
host = urlparse(endpoint).hostname
if not host or not host.endswith(".openai.azure.com"):
    print(f"VERDICT: Endpoint hostname is malformed: {host!r}")
    sys.exit(1)
resource_name = host.split(".")[0]

print(f"Endpoint: {endpoint}")
print(f"Resource name (parsed): {resource_name}")
print(f"Deployment (from env): {deployment_name}")
print(f"Subscription: {subscription_id}")

credential = DefaultAzureCredential()
client = CognitiveServicesManagementClient(credential, subscription_id)

# Step 1: Does the resource exist in this subscription?
found_resource = None
for account in client.accounts.list():
    if account.name == resource_name and account.kind == "OpenAI":
        found_resource = account
        break

if not found_resource:
    print(f"\nVERDICT: No AOAI resource named '{resource_name}' in subscription {subscription_id}.")
    print("Likely causes: (a) resource deleted, (b) wrong subscription context.")
    print("Available AOAI resources in this subscription:")
    for a in client.accounts.list():
        if a.kind == "OpenAI":
            print(f"  {a.name} in {a.location}")
    sys.exit(1)

rg = found_resource.id.split("/")[4]
print(f"Resource found: {found_resource.name} in {found_resource.location} (rg: {rg})")

# Step 2: List deployments — exact match, case-insensitive match, or nothing?
deployments = list(client.deployments.list(rg, found_resource.name))
print(f"\nDeployments on this resource ({len(deployments)}):")
for d in deployments:
    print(f"  {d.name} -> {d.properties.model.name} v{d.properties.model.version}")

exact = [d for d in deployments if d.name == deployment_name]
case_insensitive = [d for d in deployments if d.name.lower() == deployment_name.lower()]

if exact:
    print(f"\nVERDICT: Deployment '{deployment_name}' exists and endpoint matches.")
    print("This means the 404 is likely propagation (deployment created <5min ago) —")
    print("or an SDK problem. Test with curl to confirm.")
    sys.exit(0)

if case_insensitive:
    match = case_insensitive[0].name
    print(f"\nVERDICT: Case mismatch. Config says '{deployment_name}', actual is '{match}'.")
    print(f"Fix: set AZURE_OPENAI_DEPLOYMENT={match}")
    sys.exit(1)

# Step 3: No match at all
print(f"\nVERDICT: Deployment '{deployment_name}' does not exist on resource '{resource_name}'.")
print("Likely causes:")
print("  - Deployment was deleted (check ARM audit log)")
print("  - Deployment was renamed (check the deployment list above for likely candidates)")
print("  - Wrong endpoint URL (are you sure this is the right resource?)")
print("  - Terraform recreated with a new name — check Terraform state")
Run this script as part of your incident response runbook. It reduces MTTR from hours to minutes on DeploymentNotFound alerts.
Fix #2

Check the ARM audit log for delete/rename operations

Discover when and why the deployment went missing.

The Azure Activity Log records every ARM operation on the resource, including deployment deletes and renames. Query for events in the last 14 days to find who did what and when.

audit_log.sh
# List recent operations on the AOAI resource
RG=my-rg
AOAI=my-aoai-resource
RESOURCE_ID=$(az cognitiveservices account show \
  --name $AOAI -g $RG --query id -o tsv)

# All deployment-scope events in last 14 days
az monitor activity-log list \
  --resource-id $RESOURCE_ID \
  --start-time "$(date -u -d '14 days ago' +%Y-%m-%dT%H:%M:%SZ)" \
  --query "[?contains(operationName.value, 'deployments')].{time:eventTimestamp, op:operationName.value, status:status.value, caller:caller}" \
  -o table

# Deployment-specific delete events
az monitor activity-log list \
  --resource-id $RESOURCE_ID \
  --start-time "$(date -u -d '14 days ago' +%Y-%m-%dT%H:%M:%SZ)" \
  --query "[?operationName.value=='Microsoft.CognitiveServices/accounts/deployments/delete/action'].{time:eventTimestamp, caller:caller, status:status.value}" \
  -o table

# When you find the culprit event, drill in
EVENT_ID=<correlationId-from-above>
az monitor activity-log list \
  --correlation-id $EVENT_ID \
  --query "[].{op:operationName.value, caller:caller, ip:callerIpAddress, ua:properties.userAgent}"
If caller is a service principal, cross-reference with your CI/CD audit — Terraform destroys are the most common source of accidental delete.
Fix #3

Add a resource-lock to prevent accidental deletion

Cheap protection against the highest-impact case.

Add a CanNotDelete lock at the resource-group or resource level. This blocks delete operations from anyone (including Owner-role users) unless the lock is removed first. Adds a two-step to intentional deletes; blocks accidental ones.

add_lock.sh
# Lock the entire AOAI resource against delete
az lock create \
  --name aoai-no-delete \
  --resource-group my-rg \
  --resource my-aoai-resource \
  --resource-type Microsoft.CognitiveServices/accounts \
  --lock-type CanNotDelete \
  --notes "Blocks accidental delete. Remove only for planned decommission."

# For extra safety, lock the entire resource group
az lock create \
  --name aoai-rg-no-delete \
  --resource-group my-rg \
  --lock-type CanNotDelete

# Verify
az lock list --resource-group my-rg -o table

# Verify Terraform respects the lock (if using Terraform)
# You may need "prevent_destroy = true" in the AOAI resource block
Locks do NOT prevent deployment-level operations (create/update/delete). For that, use IAM: revoke Cognitive Services Contributor from anyone who does not need to modify deployments.

Prevention checklist

Ship these seven safeguards once and this error stops appearing in your logs.

  • Add CanNotDelete locks to production AOAI resources and their resource groups.
  • Set up Azure Monitor alerts on Microsoft.CognitiveServices/accounts/deployments/delete operations.
  • Never grant Cognitive Services Contributor at the subscription level — scope to the specific resource.
  • Add a startup health check that verifies the configured deployment exists before serving traffic.
  • Version deployment names (gpt4o-prod-v1, gpt4o-prod-v2) — renames leave a paper trail.
  • Use IaC with drift detection so accidental portal changes surface within hours.
  • Document deployment name conventions and stick to them — case-sensitivity + typos are almost all preventable.

Frequently asked questions

No. Deployments are not soft-deleted. Once deleted, they are gone. You must recreate. This is why locks and IAM scoping matter.
The deployment is the runtime binding; the fine-tuned model is a separate artifact. Deleting the deployment does not delete the model — you can redeploy it. However, deleting the fine-tuned model itself is destructive and requires re-training.
No — deployment names are immutable. To "rename" you must create a new deployment with the new name and remove the old one. Do this in that order and update client config in between.
The portal is a lagging view — cached, refreshed on interaction. If a deployment was deleted via CLI/ARM, the portal may show it as active for several minutes. Trust the ARM API over the portal.
AOAI resources themselves are soft-deleted for 48 hours after account deletion — they can be recovered via az cognitiveservices account recover. Deployments inside them are NOT recovered as part of this operation and must be recreated after account recovery.

Get the weekly AI-error digest

New fixes, provider status recaps, and one deep tutorial — every Tuesday. 8,400+ engineers.