GitHub Actions Boolean Input Runs When False? Use the inputs Context
A GitHub Actions job can run even when a workflow_dispatch checkbox is cleared. The usual cause is an if condition that reads github.event.inputs.deploy: the event payload contains the string 'false', and a non-empty string is truthy in an Actions conditional. Use the typed inputs.deploy Boolean instead.
The primary rule is small but important: define the input as boolean, then use inputs.<name> in an if expression. Reserve github.event.inputs for cases where you intentionally need its string representation.
Define the deploy decision as a boolean
workflow_dispatch lets a workflow expose typed inputs in the Actions UI. For an operation that writes to a deployment environment, make the decision explicit and default it to false:
name: Manual deploy
on: workflow_dispatch: inputs: deploy: description: 'Deploy the current ref?' required: true default: false type: boolean environment: description: 'Target environment' required: true type: environment
jobs: deploy: if: ${{ inputs.deploy }} runs-on: ubuntu-latest environment: ${{ inputs.environment }} steps: - uses: actions/checkout@v4 - run: ./scripts/deploy.shGitHub documents boolean and environment as supported workflow_dispatch input types. The workflow file must be on the repository’s default branch before the manual trigger is available. If this is a production workflow, keep the environment protections and permissions in the same review—not in a later cleanup.
Why github.event.inputs can run the wrong branch
Both contexts receive the submitted values, but their types differ. inputs.deploy remains a Boolean; github.event.inputs.deploy becomes the string 'true' or 'false'.
jobs: safe-deploy: if: ${{ inputs.deploy }} runs-on: ubuntu-latest steps: - run: ./scripts/deploy.sh
unsafe-deploy: if: ${{ github.event.inputs.deploy }} runs-on: ubuntu-latest steps: - run: ./scripts/deploy.shIn the second job, a non-empty string is not the same thing as the Boolean value false. The practical result is a condition that is harder to reason about and easy to copy into a deployment workflow by mistake. GitHub’s workflow syntax reference explicitly distinguishes the two contexts and calls out Boolean preservation in inputs.
If you inherit a workflow that cannot switch contexts immediately, compare the event value as a string instead of relying on its truthiness:
if: ${{ github.event.inputs.deploy == 'true' }}That is a compatibility measure, not the preferred shape for new manually triggered workflows.
Pick the condition by input source
The right expression depends on where the value came from. Do not treat every value named deploy as a Boolean.
| Value source | Runtime type | Condition to use |
|---|---|---|
workflow_dispatch input through inputs.deploy | Boolean | if: ${{ inputs.deploy }} |
The same input through github.event.inputs.deploy | String | if: ${{ github.event.inputs.deploy == 'true' }} |
A string stored in env.DEPLOY | String | if: ${{ fromJSON(env.DEPLOY) }} when the value is valid JSON true or false |
A workflow shared by workflow_dispatch and workflow_call | Typed through the unified inputs context | Prefer if: ${{ inputs.deploy }} |
GitHub’s expression rules list false, empty strings, zero, and null as falsy. The string 'false' is not on that list: it is a non-empty string. That distinction explains why this condition can select a job even though the Actions form showed a cleared checkbox:
if: ${{ github.event.inputs.deploy }}Use fromJSON() only at a boundary where you genuinely receive a string, such as an environment variable or step output. Do not add conversion around inputs.deploy; it is already a Boolean.
Keep strings at the command boundary
Shell environment variables are strings, even when the expression that supplies one came from a Boolean input. Put the typed decision in the job or step if; pass values to a command only after the job is already selected.
jobs: deploy: if: ${{ inputs.deploy }} runs-on: ubuntu-latest steps: - name: Deploy selected environment env: TARGET_ENVIRONMENT: ${{ inputs.environment }} run: ./scripts/deploy.sh "$TARGET_ENVIRONMENT"This separates two jobs that are often confused: the expression decides whether deployment is permitted to run, while the shell variable supplies an argument to a command that is already running. Treating a shell variable as the source of the deploy decision brings the string-conversion problem back.
Reproduce the type difference without deploying
Before attaching a real deploy command, add a temporary job that prints both values through toJSON(). The JSON form makes the type difference visible: the typed context prints false, while the event context prints "false".
jobs: inspect-inputs: runs-on: ubuntu-latest steps: - name: Show submitted input objects run: | printf 'inputs.deploy=%s\n' '${{ toJSON(inputs.deploy) }}' printf 'github.event.inputs.deploy=%s\n' '${{ toJSON(github.event.inputs.deploy) }}'With the checkbox clear, the relevant output shape is:
inputs.deploy=falsegithub.event.inputs.deploy="false"Run the workflow twice from the Actions tab—once with the checkbox clear and once selected. This verifies both branches without executing a deployment. Then remove the diagnostic job or keep it limited to non-sensitive input names. Do not print secrets or assume GitHub masks every value that merely resembles a secret.
If this manual workflow builds a static Astro site before deployment, Dockerize an Astro Static Site: Build dist, Serve It with Caddy shows a reproducible build-and-serve boundary. Keep that build step behind the same typed deploy gate.
The decision is mechanical: use inputs.deploy when the workflow owns a typed input, compare github.event.inputs.deploy with 'true' only for compatibility, and convert string boundaries explicitly. A cleared checkbox must produce a Boolean false at the condition that guards deployment.
FAQ
Q: Can I use github.event.inputs for a workflow_dispatch checkbox?
A: Yes, but it is a string context. Compare it explicitly with 'true' if you must use it. For a new Boolean condition, use inputs.<name> so the workflow expression receives a Boolean value.
Q: Why does the string ‘false’ run a GitHub Actions job?
A: GitHub Actions conditionals treat non-empty strings as truthy. The event payload stores a manual Boolean input as the string 'false', so use the typed inputs context or compare the event string explicitly with 'true'.
Q: Why is the Run workflow button missing?
A: GitHub documents that workflow_dispatch receives events only when its workflow file is on the default branch. Check that branch before debugging the YAML input definition.
References:
GitHub Docs: Workflow syntax for GitHub Actions
GitHub Docs: Contexts reference
Report a typo or broken link, or suggest a related topic.