Skip to content

dispatch-workflow: per-call ref override lands in inputs, causing 422 "Unexpected inputs provided" and/or dispatching the wrong ref #50027

Description

@blozano-tt

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.mdsilencer.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:

  1. 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.
  2. 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

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions