Azure / Azure/azure-dev

azd ai agent init: unified azure.yaml path defers model config without saying so, and claims --no-prompt when it wasn't passed

Open
#9,730 1 comment 0 reactions 0 assignees View on GitHub
area/core-cli bug customer-reported ext-agents
Dominant language
Go
Stars
569
Forks
364
Avg merge
2d 19h
Merged PRs (30d)
136

Description

### Summary

`azd ai agent init -m ` can silently produce a project missing
`AZURE_AI_MODEL_DEPLOYMENT_NAME`, which fails later at agent runtime with a `KeyError`.

Two distinct defects:

1. **The deferral is reported as `--no-prompt` even when that flag was never passed.**
2. **The unified `azure.yaml` path suppresses the "model configuration was deferred" warning** that
the legacy `agent.manifest.yaml` path prints, because of a hardcoded `false`.

### Defect 1 — misleading message, and it isn't limited to `--no-prompt`

Running **without** `--no-prompt`, on a pty (`pty.fork`, so stdin/stdout/stderr are all char
devices), no `CI` variable set:

```
Missing Azure environment values: AZURE_SUBSCRIPTION_ID, AZURE_LOCATION. Continuing because --no-prompt was specified.
```

No flag was specified. The user is told they asked for this, which sends them looking for a flag
they never passed rather than at their Azure context.

The real gate is `InteractiveInfo.CanPrompt()` (`pkg/azdext/tui.go`):

```go
func (i InteractiveInfo) CanPrompt() bool {
return i.StdinTTY && i.StdoutTTY && !i.NoPrompt && !i.CI && !i.Agent
}
```

`i.Agent` is set from `agentEnvVars`:

```go
var agentEnvVars = []string{
"CLAUDE_CODE", "CLAUDE_CODE_ENTRYPOINT", "GITHUB_COPILOT_CLI", "GH_COPILOT",
"GEMINI_CLI", "GEMINI_CLI_NO_RELAUNCH", "OPENCODE", "AZURE_DEV_AGENT_TYPE",
}
```

So **CI pipelines and AI-coding-agent sessions are forced down this path with no flag**, and given
how many users now drive `azd` through a coding agent, that is a meaningful blast radius. The
message should name the detected reason (non-TTY / CI / agent / explicit flag) instead of asserting
`--no-prompt`.

### Defect 2 — the deferred-model warning is suppressed on the azure.yaml path

Both paths call the same helper; only one reports model deferral:

```go
// init_foundry_project_setup.go:96 — unified azure.yaml (adopt) path
configureDeferredInitAzureContext(ctx, azdClient, envName, azureContext, false)
// ^^^^^ always false
```

```go
// init.go:2474 — legacy agent.manifest.yaml path
hasModelResources := manifestHasModelResources(agentManifest)
configureDeferredInitAzureContext(ctx, ..., hasModelResources)
```

That flag gates this block in `configureDeferredInitAzureContext`:

```go
if hasModelResources {
"Model resource configuration was deferred because model lookup requires the missing Azure values."
"Set the missing values, then re-run init to resolve model deployments automatically."
}
```

`manifestHasModelResources` reads `AgentManifest.Resources`, only populated for the legacy
`template:` manifest — so a unified `azure.yaml` reports `false` regardless, even when its
`services..deployments:` block was in fact deferred.

### Repro

```sh
mkdir probe && cd probe
azd ai agent init \
-m "https://github.com/microsoft-foundry/foundry-samples/blob/main/samples/python/hosted-agents/agent-framework/responses/01-basic/azure.yaml" \
--agent-name probe-agent
```

`.azure//.env` has no `AZURE_AI_MODEL_DEPLOYMENT_NAME`, while the generated `azure.yaml` still
contains `value: ${AZURE_AI_MODEL_DEPLOYMENT_NAME}` — it resolves to empty, so `main.py`'s
`os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"]` raises `KeyError` at run time.

The identical command in a directory whose azd env already has `AZURE_SUBSCRIPTION_ID` and
`AZURE_LOCATION` sets it correctly to `gpt-5.4-mini`. That is the only differing variable.

Verified on `azd` 1.31.2, `azure.ai.agents` 1.0.0-beta.11, across three harnesses: `--no-prompt`;
piped stdin without the flag; and `pty.fork` without the flag. All three defer identically.

### Scope I could not verify

I have no genuine interactive terminal in my environment, so I could **not** confirm the behaviour
for a human at a real TTY. Reading `CanPrompt()`, that case should prompt for subscription/location
and set the variable correctly. Treat the interactive path as unverified here rather than as known
good or known broken.

### Why the printed remediation doesn't recover

Setting `AZURE_SUBSCRIPTION_ID` / `AZURE_LOCATION` afterwards doesn't re-run deployment
verification, and `deploymentEnvUpdate` (`listen.go`) writes only `AI_PROJECT_DEPLOYMENTS` — never
`AZURE_AI_MODEL_DEPLOYMENT_NAME`. The project stays broken until `init` is re-run or the variable is
set by hand, and the message suggests neither.

### Suggested fixes

1. Report the actual reason for skipping prompts rather than blaming `--no-prompt`.
2. Base the deferral warning on what the adopted `azure.yaml` declares:
```go
hasDeployments := len(foundryDeployments(content)) > 0
configureDeferredInitAzureContext(ctx, azdClient, envName, azureContext, hasDeployments)
```
3. Optionally, when verification is skipped but `azure.yaml` declares exactly one deployment, fall
back to `persistFirstDeploymentName` with the declared name — no Azure call required, and it is
the same value verification would pick.

### Is it a regression?

Not of the adoption path — the gap has existed since it shipped. But it is a regression in effect
for anyone following the samples, because the ecosystem moved onto the path that lost the
diagnostic:

| date | change |
|---|---|
| 2026-06-24 (#8656) | Deferral warning added, including the `hasModelResources` branch |
| 2026-07-01 (#8885) | Unified `azure.yaml` adoption path added to `azd ai agent init -m` |
| 2026-07-02 (#8933) | Adoption path gains context setup, verification and `persistFirstDeploymentName`; deferral call site written with a literal `false` |
| 2026-07-07 | `foundry-samples` migrates hosted-agent samples to unified `azure.yaml` |

Earlier revisions of this issue claimed the model env-var binding was missing entirely after the
unified `azure.yaml` migration, and later that impact was limited to explicit `--no-prompt`. Both
were wrong: `persistFirstDeploymentName` handles the binding correctly when Azure context is
present, and the deferral triggers without any flag. Rescoped to the actual defects.

Contributor guide

Open the contributing guide

Research direction

Start with InteractiveInfo.CanPrompt in pkg/azdext/tui.go, then compare the call sites in init_foundry_project_setup.go and init.go for configureDeferredInitAzureContext; listen.go documents the related environment update behavior. Run the provided azd ai agent init reproduction and inspect the adopted azure.yaml deployment data. Done means skipped-prompt messages identify the actual reason, and unified azure.yaml adoption reports model deferral when deployments were deferred.

Written by the indexing model from the issue text.

Assessment

Tech stack
azure, go
Domain
ai, cli, cloud
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.