Skip to content

MCPG step fails with docker: invalid reference format. — extra \ \ after -e ADO_MCP_AUTH_TOKEN in generated YAML (v0.36.0) #1034

@jamesadevine

Description

@jamesadevine

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:

  1. a one-character argument consisting of a single space, then
  2. 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

Metadata

Metadata

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions