Summary
On v0.84.3, a dispatch-workflow safe-output call that includes the per-call ref override (landed in #49408) fails with a 422 from GitHub's REST API, because ref ends up nested inside the inputs object sent to POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches instead of being passed as that endpoint's separate top-level ref field.
Reproduction
Repo: tenstorrent/tt-metal (public), workflow .github/workflows/silencer.md → silencer.lock.yml, compiled with gh-aw v0.84.3.
Root cause
Per GitHub's REST API OpenAPI description (github/rest-api-description, descriptions/api.github.com/api.github.com.json, path /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches), ref is a required, top-level field, a sibling of inputs — never one of the inputs keys.
gh-aw's per-workflow dynamic-tool inputSchema is generated flat: it adds ref as a property alongside the target workflow's own declared inputs (visible in our compiled lock file: the single-card-demo-tests dynamic tool's inputSchema.properties includes build-type, enable-lto, enable-ops-recording, platform, ref, requested-models, run-perf-tests all as siblings).
The bug is in actions/setup/js/safe_outputs_tools_loader.cjs (confirmed at v0.84.3 and at main, lines ~165–176):
if (hasValidWorkflowMetadataName(tool._workflow_name)) {
const workflowName = tool._workflow_name.trim();
tool.handler = args => {
return handlers.defaultHandler("dispatch_workflow")({
inputs: args, // <-- args still contains `ref`
workflow_name: workflowName,
});
};
}
This passes the entire flat args object (including ref) straight through as inputs, and never reads args.ref back out as the top-level ref for the dispatch_workflow message. Two consequences:
- If the target workflow doesn't itself declare a
ref input (the normal case), GitHub rejects the whole call with 422 "Unexpected inputs provided" — this is what we hit.
- Even if a target workflow did declare a
ref input (so no 422), the top-level ref on the dispatch_workflow message would be absent and silently fall back to the default ref — so the workflow would run against the wrong branch, not the one the agent intended to validate. The feature would appear to "work" but validate nothing.
Regression history
This exact loader code path existed in v0.84.2 too, but v0.84.2's dynamic-tool schema didn't expose ref at all — a separate bug, #49713 ("per-workflow tool schema never exposes the ref parameter added in #49408"), which was closed as fixed on 2026-08-02. That fix added ref to the flat schema but didn't update the loader to strip it back out before building inputs — so v0.84.3 converts a previously-dormant defect into an active 422 (or silent wrong-ref dispatch).
Suggested fix
Destructure ref out of args in the handler before building inputs:
tool.handler = args => {
const { ref, ...inputs } = args;
return handlers.defaultHandler("dispatch_workflow")({
...(ref && { ref }),
inputs,
workflow_name: workflowName,
});
};
Worth double-checking whether call_workflow (~lines 189–200) and dispatch_repository (~lines 178–187) share the same inputs: args pattern — if their generated schemas also inject ref (or similar non-input metadata fields), they'd have the same class of bug.
Environment
- gh-aw version: v0.84.3 (also present at
main as of this report)
- Compiled via
gh aw compile in tenstorrent/tt-metal
- Engine: copilot
Summary
On v0.84.3, a
dispatch-workflowsafe-output call that includes the per-callrefoverride (landed in #49408) fails with a 422 from GitHub's REST API, becauserefends up nested inside theinputsobject sent toPOST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatchesinstead of being passed as that endpoint's separate top-levelreffield.Reproduction
Repo:
tenstorrent/tt-metal(public), workflow.github/workflows/silencer.md→silencer.lock.yml, compiled with gh-aw v0.84.3.dispatch_workflowtool call targetingsingle-card-demo-testswithref: "silencer/dedupe-matmul-memconfig-mismatch-warning-77496cdc6bbbe3a9"(its own new PR branch, from PR #51894), per the documented per-workflow dynamic-tool contract.Root cause
Per GitHub's REST API OpenAPI description (
github/rest-api-description,descriptions/api.github.com/api.github.com.json, path/repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches),refis a required, top-level field, a sibling ofinputs— never one of theinputskeys.gh-aw's per-workflow dynamic-tool
inputSchemais generated flat: it addsrefas a property alongside the target workflow's own declared inputs (visible in our compiled lock file: thesingle-card-demo-testsdynamic tool'sinputSchema.propertiesincludesbuild-type,enable-lto,enable-ops-recording,platform,ref,requested-models,run-perf-testsall as siblings).The bug is in
actions/setup/js/safe_outputs_tools_loader.cjs(confirmed at v0.84.3 and atmain, lines ~165–176):This passes the entire flat
argsobject (includingref) straight through asinputs, and never readsargs.refback out as the top-levelreffor thedispatch_workflowmessage. Two consequences:refinput (the normal case), GitHub rejects the whole call with 422 "Unexpected inputs provided" — this is what we hit.refinput (so no 422), the top-levelrefon thedispatch_workflowmessage would be absent and silently fall back to the default ref — so the workflow would run against the wrong branch, not the one the agent intended to validate. The feature would appear to "work" but validate nothing.Regression history
This exact loader code path existed in v0.84.2 too, but v0.84.2's dynamic-tool schema didn't expose
refat all — a separate bug, #49713 ("per-workflow tool schema never exposes the ref parameter added in #49408"), which was closed as fixed on 2026-08-02. That fix addedrefto the flat schema but didn't update the loader to strip it back out before buildinginputs— so v0.84.3 converts a previously-dormant defect into an active 422 (or silent wrong-ref dispatch).Suggested fix
Destructure
refout ofargsin the handler before buildinginputs:Worth double-checking whether
call_workflow(~lines 189–200) anddispatch_repository(~lines 178–187) share the sameinputs: argspattern — if their generated schemas also injectref(or similar non-input metadata fields), they'd have the same class of bug.Environment
mainas of this report)gh aw compileintenstorrent/tt-metal