deploy-managed-agent.sh reports success (exit 0, empty bodies) when an env-var value is refused

Author: akhilesharoraCreated May 29, 2026Updated May 29, 2026

deploy-managed-agent.sh runs under set -euo pipefail, but a refused env-var substitution does not stop it: the deploy exits 0 with zero agent bodies instead of failing.

yaml2json's SAFE allowlist (^[A-Za-z0-9._/:@-]*$) excludes ?, &, =, so a connector URL carrying a ?token= query string is refused via sys.exit. That refusal should abort the deploy. But the failing assignment is nested three deep in command substitutions - json=$(yaml2json ...) inside json=$(resolve_manifest ...) inside OUT=$(create_agent ...) - and set -e does not propagate a failed assignment out of a command substitution. So resolve_manifest continues with an empty json and the deploy reports success with nothing in it.

Repro (HEAD 248331e):

bash
$ BOX_MCP_URL='https://mcp.box.com/sse?token=t' GDRIVE_MCP_URL='https://g' \
    IMANAGE_MCP_URL='https://i' DEFINELY_MCP_URL='https://d' REPO_SLUG=claude-for-legal \
    bash scripts/deploy-managed-agent.sh diligence-grid --dry-run
refusing ${BOX_MCP_URL}: value contains characters outside [A-Za-z0-9._/:@-]
# --dry-run: resolved POST /v1/agents bodies (subagents first, orchestrator last)
[]
EXIT=0

A query-string-free URL yields the real 5 bodies, so the only difference is the refused value being swallowed. test-cookbooks.sh then iterates the empty list and prints ✓ ... 0 bodies, so the documented validation gate green-lights a cookbook that produced nothing. The non-dry-run path eventually errors, but with POST /v1/agents failed for : instead of the refusal reason.

Expected: a refused substitution aborts the deploy with a non-zero exit and surfaces the refusing ${VAR} reason; it never reports success or emits empty/partial output.

Source: anthropics/claude-for-legal