Skip to content

rewire-pipeline fails with 400 Bad Request for classic (designer) pipelines #1525

@mulana

Description

@mulana

The ado2gh rewire-pipeline command fails with HTTP 400 (Bad Request) on Classic (Designer) pipelines because the rewiring logic unconditionally sets settingsSourceType = 2 (YAML definitions) in the payload sent back to the ADO Build Definitions API. Classic pipelines use a Designer process (process.type = 1) and don't have YAML definitions, so the ADO API rejects the update with a 400.

Root Cause

In AdoPipelineTriggerService.cs line 232, the BuildPipelinePayload method hardcodes:

payload["settingsSourceType"] = 2;
  • settingsSourceType = 1 means "pipeline settings are defined in the UI" (classic/designer pipelines) -process.type = 1
  • settingsSourceType = 2 means "pipeline settings come from a YAML file" (YAML pipelines) - process.type = 2

For classic pipelines, there is no YAML file - the build steps are stored as designer JSON inside ADO. Setting settingsSourceType = 2 tells ADO to look for a YAML file that doesn't exist, resulting in a 400 Bad Request.

Note: the loop above this line already copies the original settingsSourceType from the pipeline definition into the payload, but line 232 unconditionally overwrites it.

Reproduction

  1. Create a classic (designer) pipeline in Azure DevOps
  2. Run: gh ado2gh rewire-pipeline --ado-org <org> --ado-team-project <project> --ado-pipeline <classic-pipeline-name> --github-org <org> --github-repo <repo> --service-connection-id <id>
  3. Observe: [WARNING] HTTP error retrieving pipeline <id> in <org>/<project>: Response status code does not indicate success: 400 (Bad Request).. Skipping pipeline rewiring.

YAML pipelines in the same project succeed.

Customer Impact

One customer has 38k pipelines, of which 14k are classic pipelines. All classic pipelines fail to rewire.

CLI version: v1.26.0 (also likely affects v1.27.0 since the code is unchanged).

Suggested Fix

  1. Preserve the original settingsSourceType from the pipeline definition instead of overriding it:
// Preserve original settingsSourceType (classic=1, YAML=2)
payload["settingsSourceType"] ??= 2;

This keeps 1 for classic pipelines and defaults to 2 only if no value was present.

  1. The process.type field is already present in the pipeline definition response from _apis/build/definitions/{pipelineId}. The fix is to:
  • Read process.type from the fetched definition data
  • Set settingsSourceType = 1 (UI/Designer) for Classic pipelines and settingsSourceType = 2 (YAML) for YAML pipelines

Similarly, the RestorePipelineToAdoRepo method in AdoApi.cs hardcodes settingsSourceType = 1, which should also be reviewed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions