Summary
Compiled pipelines that use the built-in azure-devops MCP tool fail at the Start MCP Gateway (MCPG) step with docker: invalid reference format. The MCPG container never starts, the /health poll loop runs out, and the Agent job exits 1 (Bash exited with code '1').
Root cause: start_mcpg_step in src/compile/agentic_pipeline.rs double-appends a \ line continuation to the docker-env flag emitted by generate_mcpg_docker_env. The emitted YAML contains a literal \ \ token, which bash interprets as a one-character argument (a single space) inserted between -e ADO_MCP_AUTH_TOKEN=… and the image, so docker run consumes that space as the image reference.
Affects: every agent whose front matter declares tools.azure-devops (or anything else that contributes an extension pipeline_env mapping). In our repository this took down five pipelines simultaneously after a routine ado-aw compile upgrade to v0.36.0.
Observed Run
- Build: https://dev.azure.com/msazuresphere/4x4/_build/results?buildId=613512
- Definition:
ado-aw reviewer (compiled from agents/pr-reviewer.md)
- Source branch:
refs/heads/jamesadevine/test-pr-reviewer-bait
- Result:
failed after 2m 25s
- Failed step: Start MCP Gateway (MCPG) (timeline record
691d60ec-3d15-5b34-11ae-186526790c53, task log 43)
- Compiler version:
ado-aw 0.36.0 (verified via ado-aw --version; aw_info.json.compiler_version = "0.36.0")
- MCPG image:
ghcr.io/github/gh-aw-mcpg:v0.3.25
Failing step log (verbatim, lines 51-55)
2026-06-15T20:17:15.6783133Z MCPG started (PID: 2650)
2026-06-15T20:17:15.6949924Z docker: invalid reference format.
2026-06-15T20:17:15.6951878Z See 'docker run --help'.
2026-06-15T20:17:45.9518262Z
2026-06-15T20:17:45.9535713Z ##[error]Bash exited with code '1'.
(The 30s gap is the /health poll that times out because the docker container never started.)
ado-aw audit 613512 --json corroborates: mcpg/stderr.log contains exactly two lines — docker: invalid reference format. / See 'docker run --help'. — and safe_outputs.ndjson is empty (the agent never ran).
Generated YAML (pr-reviewer.yml, lines 475-486)
echo "$MCPG_CONFIG" | docker run -i --rm \
--name mcpg \
--network host \
--entrypoint /app/awmg \
-v /var/run/docker.sock:/var/run/docker.sock \
-e MCP_GATEWAY_PORT="$(MCP_GATEWAY_PORT)" \
-e MCP_GATEWAY_DOMAIN="$(MCP_GATEWAY_DOMAIN)" \
-e MCP_GATEWAY_API_KEY="$(MCP_GATEWAY_API_KEY)" \
-e ADO_MCP_AUTH_TOKEN="$SC_READ_TOKEN" \ \ # ← BUG: trailing "\ \"
ghcr.io/github/gh-aw-mcpg:v0.3.25 \
--routed --listen 0.0.0.0:80 --config-stdin --log-dir /tmp/gh-aw/mcp-logs \
> "$GATEWAY_OUTPUT" 2> >(tee /tmp/gh-aw/mcp-logs/stderr.log >&2) &
In bash, \ is an escaped space (literal space character treated as part of an argument) and \<NL> is a line continuation. Sequence \ \<NL> therefore produces:
- a one-character argument consisting of a single space, then
- the line continuation.
docker run’s positional-argument parser scans for the first non-option token and treats it as IMAGE. That token is " " (a single space) — an invalid reference. The actual ghcr.io/github/gh-aw-mcpg:v0.3.25 is then parsed as the COMMAND.
Removing the stray space-escape (i.e. just -e ADO_MCP_AUTH_TOKEN="$SC_READ_TOKEN" \) makes the docker invocation valid again.
Root cause in ado-aw
The duplication happens in start_mcpg_step (src/compile/agentic_pipeline.rs, ~lines 1334-1369 in v0.36.0). It reads the value returned by generate_mcpg_docker_env and re-wraps every line:
// src/compile/agentic_pipeline.rs (v0.36.0)
let docker_env_lines: String =
if mcpg_docker_env.trim().is_empty() || mcpg_docker_env.trim() == "\\" {
"\\\n \\".to_string()
} else {
mcpg_docker_env
.lines()
.map(|l| format!("{l} \\")) // <-- appends ` \` to every line
.collect::<Vec<_>>()
.join("\n ")
};
But generate_mcpg_docker_env (src/compile/common.rs, ~lines 2034-2092) already appends a trailing \ to its returned string:
// src/compile/common.rs (v0.36.0)
env_flags.sort();
if env_flags.is_empty() {
"\\".to_string()
} else {
let flags = env_flags.join(" \\\n");
format!("{} \\", flags) // <-- already ends with ` \`
}
So for a single-entry input (-e ADO_MCP_AUTH_TOKEN="$SC_READ_TOKEN" \), .lines() yields one line ending in \, and format!("{l} \\") appends a second \ → -e ADO_MCP_AUTH_TOKEN="$SC_READ_TOKEN" \ \.
For multi-entry inputs the same defect would corrupt only the last flag, but in the common ADO-tool case there’s exactly one extension-contributed flag, so this is the line that breaks.
Reproduction
Minimal agent.md:
---
name: repro
description: repro
on:
workflow_dispatch:
permissions:
read: any-arm-service-connection
tools:
azure-devops:
toolsets:
- repos
safe-outputs:
add-pr-comment:
max: 1
---
Stub.
ado-aw compile agent.md
grep -n 'ADO_MCP_AUTH_TOKEN' agent.yml
# → -e ADO_MCP_AUTH_TOKEN="$SC_READ_TOKEN" \ \
Run on Azure DevOps → MCPG step fails with docker: invalid reference format.
Impact
- All compiled pipelines that include the ADO MCP tool with
permissions.read against an ARM service connection are unrunnable on ado-aw 0.36.0.
- In this repository, 5 of 5 such pipelines are affected:
ctf.yml:470, dotnet-dependency-updater.yml:392, pr-reviewer.yml:483, release-readiness.yml:464, test-pr-reviewer-bait.yml:459 — each ending in -e ADO_MCP_AUTH_TOKEN="$SC_READ_TOKEN" \ \.
- The Agent job runs for the full
/health timeout (30s) before failing, wasting build minutes and obscuring the root cause behind a generic Bash exited with code '1'.
- The MD-source markdown is unchanged from a working
ado-aw 0.35.x build; only recompiling under v0.36.0 introduced the bad output.
Suggested fix direction
Either:
- Have
generate_mcpg_docker_env return the env flags without the trailing \ (let the caller own continuations), or
- Drop the
format!("{l} \\") line wrap in start_mcpg_step and just .join("\n ") the already-terminated lines.
A regression test that compiles a minimal agent with an ADO tool + ARM service connection and asserts the emitted YAML never contains the literal substring \ \ (or, more strictly, runs bash -n against the rendered step body) would prevent reintroduction.
Diagnostic artifacts
aw_info.json: {"compiler_version":"0.36.0","engine":"copilot","model":"claude-opus-4.7","source":"agents/pr-reviewer.md","target":"standalone"}
mcpg-config.json (sanitized): standard ADO MCP entry (container: node:20-slim, entrypoint: npx, args -y @azure-devops/mcp msazuresphere -d repos -a envvar) + HTTP safeoutputs entry.
audit-613512.json (overview): Setup succeeded (26s), Agent failed (1m38s), no Detection / SafeOutputs jobs scheduled because Agent stage failed.
- Full step log: https://dev.azure.com/msazuresphere/4881b890-93a9-4bcf-95c9-d07c192a073a/_apis/build/builds/613512/logs/43
Summary
Compiled pipelines that use the built-in
azure-devopsMCP tool fail at the Start MCP Gateway (MCPG) step withdocker: invalid reference format.The MCPG container never starts, the/healthpoll loop runs out, and the Agent job exits 1 (Bash exited with code '1').Root cause:
start_mcpg_stepinsrc/compile/agentic_pipeline.rsdouble-appends a\line continuation to the docker-env flag emitted bygenerate_mcpg_docker_env. The emitted YAML contains a literal\ \token, which bash interprets as a one-character argument (a single space) inserted between-e ADO_MCP_AUTH_TOKEN=…and the image, sodocker runconsumes that space as the image reference.Affects: every agent whose front matter declares
tools.azure-devops(or anything else that contributes an extensionpipeline_envmapping). In our repository this took down five pipelines simultaneously after a routineado-aw compileupgrade to v0.36.0.Observed Run
ado-aw reviewer(compiled fromagents/pr-reviewer.md)refs/heads/jamesadevine/test-pr-reviewer-baitfailedafter 2m 25s691d60ec-3d15-5b34-11ae-186526790c53, task log 43)ado-aw 0.36.0(verified viaado-aw --version;aw_info.json.compiler_version = "0.36.0")ghcr.io/github/gh-aw-mcpg:v0.3.25Failing step log (verbatim, lines 51-55)
(The 30s gap is the
/healthpoll that times out because the docker container never started.)ado-aw audit 613512 --jsoncorroborates:mcpg/stderr.logcontains exactly two lines —docker: invalid reference format./See 'docker run --help'.— andsafe_outputs.ndjsonis empty (the agent never ran).Generated YAML (pr-reviewer.yml, lines 475-486)
In bash,
\is an escaped space (literal space character treated as part of an argument) and\<NL>is a line continuation. Sequence\ \<NL>therefore produces:docker run’s positional-argument parser scans for the first non-option token and treats it asIMAGE. That token is" "(a single space) — an invalid reference. The actualghcr.io/github/gh-aw-mcpg:v0.3.25is then parsed as theCOMMAND.Removing the stray space-escape (i.e. just
-e ADO_MCP_AUTH_TOKEN="$SC_READ_TOKEN" \) makes the docker invocation valid again.Root cause in
ado-awThe duplication happens in
start_mcpg_step(src/compile/agentic_pipeline.rs, ~lines 1334-1369 in v0.36.0). It reads the value returned bygenerate_mcpg_docker_envand re-wraps every line:But
generate_mcpg_docker_env(src/compile/common.rs, ~lines 2034-2092) already appends a trailing\to its returned string:So for a single-entry input (
-e ADO_MCP_AUTH_TOKEN="$SC_READ_TOKEN" \),.lines()yields one line ending in\, andformat!("{l} \\")appends a second\→-e ADO_MCP_AUTH_TOKEN="$SC_READ_TOKEN" \ \.For multi-entry inputs the same defect would corrupt only the last flag, but in the common ADO-tool case there’s exactly one extension-contributed flag, so this is the line that breaks.
Reproduction
Minimal
agent.md:Run on Azure DevOps → MCPG step fails with
docker: invalid reference format.Impact
permissions.readagainst an ARM service connection are unrunnable onado-aw 0.36.0.ctf.yml:470,dotnet-dependency-updater.yml:392,pr-reviewer.yml:483,release-readiness.yml:464,test-pr-reviewer-bait.yml:459— each ending in-e ADO_MCP_AUTH_TOKEN="$SC_READ_TOKEN" \ \./healthtimeout (30s) before failing, wasting build minutes and obscuring the root cause behind a genericBash exited with code '1'.ado-aw 0.35.xbuild; only recompiling under v0.36.0 introduced the bad output.Suggested fix direction
Either:
generate_mcpg_docker_envreturn the env flags without the trailing\(let the caller own continuations), orformat!("{l} \\")line wrap instart_mcpg_stepand just.join("\n ")the already-terminated lines.A regression test that compiles a minimal agent with an ADO tool + ARM service connection and asserts the emitted YAML never contains the literal substring
\ \(or, more strictly, runsbash -nagainst the rendered step body) would prevent reintroduction.Diagnostic artifacts
aw_info.json:{"compiler_version":"0.36.0","engine":"copilot","model":"claude-opus-4.7","source":"agents/pr-reviewer.md","target":"standalone"}mcpg-config.json(sanitized): standard ADO MCP entry (container: node:20-slim,entrypoint: npx, args-y @azure-devops/mcp msazuresphere -d repos -a envvar) + HTTP safeoutputs entry.audit-613512.json(overview): Setupsucceeded(26s), Agentfailed(1m38s), no Detection / SafeOutputs jobs scheduled because Agent stage failed.