Reference
release.yaml, and every rule it is checked against
One file in your repository describes the pipeline. Credentials, store accounts, distribution targets and approval policies are deliberately not in it — they live in the platform, which is what makes moving a project between store accounts possible.
A complete file
version: 1
framework: flutter
mode: native
apps:
ios:
bundle_id: com.example.app
android:
package: com.example.app
workflows:
pr:
triggers:
pull_request:
branches: [main]
priority: pr-check
jobs:
test:
steps:
- uses: core/checkout
- uses: flutter/bootstrap
- uses: flutter/test
build-ios:
needs: [test]
app: ios
runs_on: { os: darwin }
steps:
- uses: core/checkout
- uses: flutter/build
with:
platform: ios
signed: false # unsigned: no credential is resolved at all
archive: falseThat pull-request workflow is fork-safe, and not because anything says so. signed: false means the iOS build resolves no signing credential, no step in the job distributes, and so allowed_on_untrusted derives true. The step library shows that derivation for all forty-two steps.
28 errors
A file with any of these does not run.
E001The document does not match the schema
Structural: a wrong type, an unknown key, a value outside an enum. Reported before any semantic rule runs, because running semantics over a malformed document produces cascading nonsense.
E010needs names a job that does not exist
Usually a rename that missed one reference. The graph is built from these names, so an unresolvable one is a job that can never start.
E011The job graph has a cycle
Two or more jobs waiting on each other. The engine would never schedule any of them, and it would look like a queue problem rather than a config one.
E012A job depends on itself
The degenerate cycle, called out separately because the fix is obvious once named.
E013Two steps in one job share an id
Step ids are how one step references another's output. Two steps with one id makes that reference ambiguous.
E014Retired: an expression referenced an unknown step id
Never emitted. It described an error inside a `${{ }}` expression, and Conductor does not evaluate expressions — E017 refuses them outright. The code is kept so it is never reused for something else.
E015Retired: an expression referenced a job not in needs
Never emitted, for the same reason as E014: there are no expressions for it to be about. Kept reserved rather than reused.
E016A field the schema accepts and Conductor does not implement
`if:`, `outputs:`, `strategy.fail_fast: true` and `strategy.max_parallel` are part of the file format and nothing acts on them. Accepting them silently meant a job with `if: false` ran anyway; refusing them means nobody builds on a behaviour that is not there.
E017A `${{ }}` expression, which nothing evaluates
Conductor has no expression language, deliberately: a job's credential set must be computable by reading the file. An expression would be passed along as literal text. Matrix values arrive as CONDUCTOR_MATRIX_<KEY> environment variables, the run's ref and SHA as CONDUCTOR_GIT_REF and CONDUCTOR_GIT_SHA, and a concurrency group is already scoped to its ref.
E020uses names a step that is not in the registry
The registry is the whole step library, so this is a typo or a version of Conductor older than the step. The diagnostic suggests the nearest name.
E021The step exists; that major version does not
Steps are versioned per major. Pinning one that was never published fails here rather than at dispatch.
E022`with` violates the step's input schema
Each step declares a JSON Schema for its inputs, and it is checked here rather than by the step failing on the machine.
E023A required input is absent
Separated from the schema error above because “you forgot one” and “this value is wrong” are different problems with different fixes.
E030A distribution step is in a pull-request workflow
A pull request must not be able to ship anything. This is an error rather than a warning because the failure mode is a build reaching testers from an unreviewed branch.
E031core/shell is in a job that also distributes
A shell step in a job holding store credentials is the shape of an incident: arbitrary commands beside standing, high-privilege material. Split the shell work into a job the distribution job depends on.
E032A step needing a sensitive credential is on the untrusted pool
The untrusted pool is where a fork's pull request runs, and it never receives standing credentials. A clone token is not sensitive and does not trip this — a fork may check out the code it is testing.
E033A fastlane lane needing credentials is reachable from a fork
An adopted Fastfile is arbitrary Ruby holding injected credentials. Running it credential-free on a fork fails in a way that looks like Conductor broke it, so it is refused instead.
E034A job resolves credentials and does not say which app it targets
Which credential satisfies a purpose is resolved per app. Without `app: ios` or `app: android` there is nothing to resolve against, and the build would have to guess.
E035One job mixes runner steps with control-plane ones
A job runs entirely on one side. Mixing them would mean a workspace that exists for half the steps in it.
E036A job selects a variant that is not declared
`variant:` names one of apps.<app>.variants — another build of the app that is a separate app at the stores, with its own bundle id or package. It needs an `app:` to be a variant of, and the variant has to be declared there; otherwise there is no identifier to sign or ship it under, and the run would fail minutes in instead of here.
E037A pull-request workflow asks for a signed build
Pull request builds are never signed or archived — Conductor forces both off for every pull_request run, so the build needs no credential and a fork's pull request can run at all. An explicit `signed: true` is a line claiming something that will not happen.
E039A pull-request workflow step needs signing material
A pull request's run never receives a credential that signs — not by forcing a build unsigned, which covers ios/build, android/build and flutter/build, but at the credential boundary, so ios/sign and a fastlane lane listing ios_signing or android_signing cannot reach it either. Sign in a push or manual workflow.
E038ios/build or ios/test has no Xcode scheme
xcodebuild's default scheme depends on which schemes are shared and how they sort, so the step refuses to guess. Name one on the step, or once under apps.ios.scheme; a shared scheme is a file in xcshareddata/xcschemes, which is also where Conductor's detection looks.
E040A store-account identifier appears in the config
No Apple Team ID and no Play developer id may appear in release.yaml. That rule is the entire reason a project can be moved to a different store account as a configuration change rather than a rewrite.
E041A distribution step names no target
`target` is how a step says where a build goes; the store account, credential and app behind the name are resolved at run time. The step library lists it as optional for compatibility, but no distribution can run without one, so leaving it out is refused here rather than at the last step of a pipeline.
E042The step is declared but cannot run on this version of Conductor
A control-plane step whose executor is not implemented yet. It would validate, schedule and then fail at run time, so it is refused while the file is still being written.
E052The step is in the registry but this version of Conductor does not run it
A step that was declared and turned out not to work. The registry only ever grows, so it stays listed; it is refused here, with the reason and what to do instead, rather than failing a run.
E050A fastlane step is used while mode is native
Native mode means typed steps only. Set `mode: hybrid` if you want both, which is a deliberate choice rather than an accident.
9 warnings
The pipeline runs. Each one is something that will cost you later, said now.
E051mode is adopt and no fastlane step is used
Adopt mode exists to run your existing lanes. If none are called, `mode: native` describes what is actually happening.
W060No current runner matches runs_on
The job would queue indefinitely. A warning rather than an error because a fleet changes — enrolling the right machine fixes it without touching the file.
W061The matrix expands beyond what this fleet can run
On a small fleet a wide matrix is not parallelism, it is a queue. Learning that from a warning beats learning it from a dashboard.
W062An `on:` key was parsed as the boolean true
Under YAML 1.1 an unquoted `on` is a boolean. Conductor's parser is 1.2 and keeps it a string, but other tooling may not — `triggers:` is the spelling immune to it.
W063The declared framework differs from what was detected
A warning, never a silent switch. A build that quietly changed how it was produced is worse than one that stopped.
W064paths and paths_ignore overlap
The overlap is silently excluded, which is rarely what somebody meant to write.
W065An expensive darwin job has no path filter
macOS capacity is the scarce resource. A job that rebuilds an iOS app because a README changed is spending the thing you have least of.
W066Retired: a pull-request workflow asked for a signed build
Never emitted. It was a warning, and only for an explicit `signed: true`; pull request builds are now forced unsigned, and asking for a signed one is the error E037.
W067A concurrency group does not cancel in-progress runs
A run that arrives while its group is busy is not started — Conductor does not queue runs behind a group the way GitHub Actions does. Sometimes that is the point; set cancel_in_progress: true if the newer run should replace the older one.
Codes are stable and never reused
A diagnostic code is an identifier you can search for, link a colleague to, and grep a build log for. So they are append-only: a rule that is removed leaves its number retired rather than handing it to the next rule, because a search result explaining E032 should not one day explain something else.