Bug: dispatch-workflow safe-output causes CAPIError: 400 Bad Request in strict mode
Summary
Adding a dispatch-workflow safe-output to an agentic workflow causes the Copilot CLI agent to fail immediately with CAPIError: 400 Bad Request on the first inference attempt. Removing the dispatch-workflow safe-output (with no other changes) resolves the issue.
Environment
- gh-aw version: v0.68.1 (latest)
- Agent engine: Copilot CLI (
agent_id: copilot)
- Strict mode:
true (default)
- AWF image: 0.25.18
Context
We use a label-command router pattern: a lightweight workflow watches for issues: [labeled] events and dispatches the appropriate agentic workflow via workflow_dispatch. One agent workflow (the "analyzer") evaluates items and, if they pass, needs to dispatch a second agent workflow (the "executor") to act on them. We added dispatch-workflow to the analyzer so it can chain to the executor after completing its work.
Minimal Reproduction
Working configuration (no dispatch-workflow):
---
on:
workflow_dispatch:
permissions:
contents: read
issues: read
tools:
cache-memory: true
bash: ["python3", "cat", "echo", "gh"]
github:
toolsets: [issues]
safe-outputs:
update-issue:
target: "*"
max: 10
add-labels:
allowed: [some-label]
max: 10
remove-labels:
allowed: [another-label]
max: 10
add-comment:
target: "*"
max: 10
close-issue:
target: "*"
max: 10
---
# Analyzer Workflow
Evaluate issues and take action.
Broken configuration (only change: add dispatch-workflow):
safe-outputs:
# ... same as above, plus:
dispatch-workflow:
workflows: [executor-workflow.agent]
max: 3
Where executor-workflow.agent.md exists in the same repo and declares workflow_dispatch in its on: triggers.
Observed Behavior
-
gh aw compile succeeds without errors.
-
The compiled lock file contains a dynamic_tools entry with the dispatch tool definition.
-
The workflow runs. The activation job succeeds. The agent job starts.
-
During the "Execute GitHub Copilot CLI" step, the following warning appears:
[entrypoint][WARN] Failed to transfer /host/home/runner/work/_temp/gh-aw/safeoutputs ownership to chroot user
-
The Copilot CLI immediately fails on the first inference attempt:
Execution failed: CAPIError: 400 400 Bad Request
-
The copilot-driver retries with --resume, but subsequent attempts fail with:
Error: No authentication information found.
-
After all retries are exhausted, the job fails.
Expected Behavior
The workflow should start successfully and the dispatch-workflow tool should be available to the agent, same as any other safe-output tool.
Key Observations
- The warning is the smoking gun:
Failed to transfer /host/home/runner/work/_temp/gh-aw/safeoutputs ownership to chroot user does NOT appear in working runs (without dispatch-workflow). It consistently appears in all failing runs.
- The 400 is from the Copilot Inference API, not GitHub Actions. The tool definitions (including the
dynamic_tools for dispatch) are sent as MCP tools in the initial request. If the safe-outputs MCP server can't read the tool definition file due to the chroot ownership failure, the tool schema sent to the inference API may be malformed/incomplete, causing the 400.
- Removing
dispatch-workflow is the only change needed to fix it. All other safe-outputs (add-labels, remove-labels, update-issue, add-comment, close-issue) work correctly.
- The issue is 100% reproducible. Tested 3 consecutive runs with
dispatch-workflow (all failed identically), then 1 run without it (succeeded), then re-added it (failed again).
Compiled Lock File Diff
The only meaningful differences when dispatch-workflow is added:
- A
dispatch_workflow entry in the safe-outputs config JSON
- A
dynamic_tools array entry with the dispatch tool schema
actions: write added to conclusion and safe_outputs job permissions
- "dynamic_tools": []
+ "dynamic_tools": [
+ {
+ "_workflow_name": "executor-workflow.agent",
+ "description": "Dispatch the 'executor-workflow.agent' workflow...",
+ "inputSchema": {
+ "additionalProperties": false,
+ "properties": {
+ "aw_context": {
+ "default": "",
+ "description": "Agent caller context...",
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "name": "executor_workflow.agent"
+ }
+ ]
Hypothesis
The dispatch-workflow safe-output adds a dynamic tool definition file to the safe-outputs directory. When the AWF sandbox (strict mode, chroot) tries to transfer ownership of the safe-outputs directory to the chroot user, this new file (or the directory structure change) fails the ownership transfer. The safe-outputs MCP server then starts with either a missing or unreadable tool definition, causing the Copilot Inference API to receive a malformed tool schema and return 400.
Workaround
Remove dispatch-workflow from safe-outputs. Use alternative chaining:
- A label-command router workflow that polls for command labels on a cron schedule and dispatches the appropriate workflow
- Or use
call-workflow (compile-time fan-out) if the target workflow supports workflow_call
Bug:
dispatch-workflowsafe-output causesCAPIError: 400 Bad Requestin strict modeSummary
Adding a
dispatch-workflowsafe-output to an agentic workflow causes the Copilot CLI agent to fail immediately withCAPIError: 400 Bad Requeston the first inference attempt. Removing thedispatch-workflowsafe-output (with no other changes) resolves the issue.Environment
agent_id: copilot)true(default)Context
We use a label-command router pattern: a lightweight workflow watches for
issues: [labeled]events and dispatches the appropriate agentic workflow viaworkflow_dispatch. One agent workflow (the "analyzer") evaluates items and, if they pass, needs to dispatch a second agent workflow (the "executor") to act on them. We addeddispatch-workflowto the analyzer so it can chain to the executor after completing its work.Minimal Reproduction
Working configuration (no
dispatch-workflow):Broken configuration (only change: add
dispatch-workflow):Where
executor-workflow.agent.mdexists in the same repo and declaresworkflow_dispatchin itson:triggers.Observed Behavior
gh aw compilesucceeds without errors.The compiled lock file contains a
dynamic_toolsentry with the dispatch tool definition.The workflow runs. The
activationjob succeeds. Theagentjob starts.During the "Execute GitHub Copilot CLI" step, the following warning appears:
The Copilot CLI immediately fails on the first inference attempt:
The copilot-driver retries with
--resume, but subsequent attempts fail with:After all retries are exhausted, the job fails.
Expected Behavior
The workflow should start successfully and the
dispatch-workflowtool should be available to the agent, same as any other safe-output tool.Key Observations
Failed to transfer /host/home/runner/work/_temp/gh-aw/safeoutputs ownership to chroot userdoes NOT appear in working runs (withoutdispatch-workflow). It consistently appears in all failing runs.dynamic_toolsfor dispatch) are sent as MCP tools in the initial request. If the safe-outputs MCP server can't read the tool definition file due to the chroot ownership failure, the tool schema sent to the inference API may be malformed/incomplete, causing the 400.dispatch-workflowis the only change needed to fix it. All other safe-outputs (add-labels, remove-labels, update-issue, add-comment, close-issue) work correctly.dispatch-workflow(all failed identically), then 1 run without it (succeeded), then re-added it (failed again).Compiled Lock File Diff
The only meaningful differences when
dispatch-workflowis added:dispatch_workflowentry in the safe-outputs config JSONdynamic_toolsarray entry with the dispatch tool schemaactions: writeadded toconclusionandsafe_outputsjob permissionsHypothesis
The
dispatch-workflowsafe-output adds a dynamic tool definition file to the safe-outputs directory. When the AWF sandbox (strict mode, chroot) tries to transfer ownership of the safe-outputs directory to the chroot user, this new file (or the directory structure change) fails the ownership transfer. The safe-outputs MCP server then starts with either a missing or unreadable tool definition, causing the Copilot Inference API to receive a malformed tool schema and return 400.Workaround
Remove
dispatch-workflowfrom safe-outputs. Use alternative chaining:call-workflow(compile-time fan-out) if the target workflow supportsworkflow_call