Repository navigation
az containerapp up silently re-creates an existing container app when its second existence check fails, wiping identity, ingress, secrets and scale #34193
Description
Describe the bug
az containerapp up checks twice whether the target app exists, and both checks swallow every exception:
ContainerApp.create()in_up_utils.pycallsget_container_app_if_exists(), which wrapsContainerAppClient.showin a bareexcept: pass(_utils.pyL617-L623). Its result only chooses theUpdating …/Creating …log line (_up_utils.pyL384-L391).containerapp_up_logic()then callsContainerAppClient.showagain, also inside a bareexcept: pass, and if that returns nothing it callscreate_containerapp(...)(custom.pyL3747-L3757).
So when the second GET fails for any reason other than the app not existing — a throttled or transient error, a network or token hiccup — up treats an existing app as missing and sends a full create PUT built only from its own arguments. That PUT replaces the app's configuration: everything not passed to up is reset.
We hit this in a CI deploy of an existing app. The log shows both branches in one run:
WARNING: Updating Containerapp <app> in resource group <rg>
...
Container app created. To access it over HTTPS, enable ingress: az containerapp ingress enable -n <app> -g <rg> --type external --target-port <port> --transport auto
Your container app <app> has been created and deployed! Congrats!
Container app created. To access it over HTTPS, enable ingress is printed only by ContainerAppCreateDecorator.post_process (containerapp_decorator.py L519), so the first check found the app and the second did not. Azure Resource Graph change history for that write shows:
identity.type→None— the system-assigned identity was deleted, so its principal (and every role assignment and database user granted to it) is gone for good- ingress removed
- every secret except the registry credential
uprebuilds removed, including the Easy Auth client secret; theauthConfigschild resource survived, so Easy Auth was left enabled pointing at a secret that no longer existed and the app could not start - scale settings reset to defaults
The command exited 0. The same command with the same CLI version against another existing app logged Updating Containerapp … followed by Browse to your container app at …, took the update path and left its configuration intact; the failing run took several times longer, consistent with a create.
The exception from the second GET is swallowed, so we cannot tell what it was.
Related command
az containerapp up
Errors
None — the command succeeds. That is the problem: a failed read becomes a destructive create with exit code 0.
Issue script & Debug output
Not reproducible on demand, since it needs the second show to fail. Command as run in CI:
az config set extension.use_dynamic_install=yes_without_prompt
az containerapp up \
--name <app> \
--resource-group <rg> \
--image <registry>.azurecr.io/<image>:<tag> \
--environment /subscriptions/<sub>/resourceGroups/<env-rg>/providers/Microsoft.App/managedEnvironments/<env> \
--location <location> \
--env-vars KEY1="value" KEY2="value"It can be reproduced deterministically by making the second ContainerAppClient.show call in containerapp_up_logic raise (for example by patching it in a test) against an existing app: up then calls create_containerapp.
Expected behavior
upmust not create when it cannot tell whether the app exists. Only a definite not-found (ResourceNotFound/ HTTP 404) should lead to create; any other error from the existence check should be raised.- The existence check should happen once, and the same answer should decide both the log line and the create/update branch.
- Ideally,
upon an existing app should never send a full PUT built from its own arguments, since that silently discards identity, ingress, secrets, scale and revision settings thatuphas no flags for.
Environment Summary
azure-cli 2.90.0 (GitHub-hosted ubuntu-24.04 runner)
containerapp extension: not installed
Python (Linux)
Additional context
- The bare
exceptblocks were left in place by [Containerapp] Fix #26688:az containerapp up: Fix logic about updating an existing containerapp #29336, which addressed an earlier existence-detection problem inup("az containerapp up" will sometimes use a container app environment that the container app IS NOT located in #26688). containerapp upis documented as "create or update"; nothing in the docs warns that a transient read failure turns an update into a re-create of an existing app.- We are moving our pipeline to
az containerapp updatefor existing apps and create only after a confirmed not-found, failing the run on any other lookup error.
Activity
yonzhan commentedon Oct 8, 2026
Thank you for opening this issue, we will look into it.
x-engineering-agent commentedon Oct 14, 2026
X Engineering Agent Bug Analysis
Confidence: Low (0.40)
Summary
A failed existence read during az containerapp up can select a create PUT for an existing app, replacing configuration instead of failing safely.
Likely root cause
Two independent lookups conflate every exception with absence; the second result controls create/update and can contradict the first log message.
Affected code
src/azure-cli/azure/cli/command_modules/containerapp/_up_utils.py›ContainerApp.create(line 382)src/azure-cli/azure/cli/command_modules/containerapp/_utils.py›get_container_app_if_exists(line 617)src/azure-cli/azure/cli/command_modules/containerapp/custom.py›containerapp_up_logic(line 3747)src/azure-cli/azure/cli/command_modules/containerapp/containerapp_decorator.py›ContainerAppCreateDecorator.construct_payload(line 289)src/azure-cli/azure/cli/command_modules/containerapp/_clients.py›ContainerAppClient.create_or_update(line 122)Ruled out
Proposed change
Treat only verified resource absence as permission to create and propagate every other lookup failure before writes. Reuse the existing HTTP-error handling convention; share one existence result for logging and branch selection, preserving direct callers and the normal partial-update path.
Workaround
Use az containerapp update for known existing apps; permit a separate create only after a confirmed not-found and fail on other lookup errors.
Technical details
Evidence
Test plan
Add mocked regressions alongside tests/latest/test_containerapp_up.py and test_containerapp_utils.py: found selects update, confirmed not-found selects create, 401/403/429/5xx and transport errors propagate with neither create nor update writes. Regress the first-found/second-error case and assert consistent logging/existence reuse. Preserve identity, ingress, secrets and scale on ordinary updates. Mocked fault injection needs no recordings; service configuration preservation needs a focused live scenario.
Generated code: Handwritten code. The failing built-in command logic is in containerapp/custom.py, _up_utils.py and _utils.py, outside generated AAZ output; the reporter has no containerapp extension installed.
Out of scope: No service-side fault or exact transient error is established; avoid unrelated extension changes and a general rewrite of create/update semantics.
Live reproduction (BAMI sandbox) — Inconclusive
Verdict: Inconclusive: the sandbox could not run the reproduction to the end.
Reason: the problem cannot be expressed as allowed sandbox commands.
The analysis above is based on reading the code only, so its confidence is capped at low.
Commands ran in an isolated Azure sandbox subscription from a validated ReproSpec; output is redacted.
Posted by x-engineering-agent (Fixer)