GitHub Actions Interview Questions: Workflow Debugging, Permissions, and Concurrency

Practice GitHub Actions interview questions with broken YAML, skipped jobs, token permissions, required checks, and concurrency timelines explained.

Author: PracHub

Published: 9/9/2026

GitHub Actions Interview Questions: Workflow Debugging, Permissions, and Concurrency

September 9, 2026

Quick Overview

Repair three original workflow defects and explain expected job states, minimal token permissions, and what happens when newer commits arrive.

Software EngineerFree

GitHub Actions interview questions are often debugging questions in disguise. A workflow can be valid YAML while its required check never appears, its final job is skipped, or its token cannot perform the requested write. Start by identifying which layer failed before editing the file.

This guide uses official GitHub documentation for platform behavior and original defective workflows for practice. A candidate's SRE account describes being asked about Actions syntax, but one report does not establish a universal interview format. These exercises concern the product, not GitHub's own hiring process.

The YAML and shell checks below were tested locally; expected hosted job states are documentation-derived, not a captured GitHub run. Start with Design a Dependency-Aware CI/CD Pipeline, then use these repairs to make your dependency and failure policies precise.

GitHub Actions interview preparation: events, dependencies, token permissions and concurrency

Read the workflow and the event together

Official basics: workflow YAML belongs under .github/workflows. The on configuration selects events and filters; branch and path filters can both affect eligibility. A job-level setting has a different scope from a workflow-level setting. Workflow syntax.

For each original exercise, establish four inputs before proposing a fix: the event payload, the workflow revision that applies to it, the relevant repository rules, and the job or check that the user expected. “It did not run” is too broad to diagnose.

Use this practical distinction. If there is no workflow run, inspect triggering and filters. If the run exists but a job is skipped, inspect dependencies and its condition. If a step runs and receives a permission error, inspect the credential and API operation. If work disappears when a newer commit arrives, inspect concurrency and cancellation.

Keep the original run ID and commit SHA in your notes. A repaired file on a different branch is not evidence that the earlier run used the repair. When a teammate reports success, compare the event and revision before concluding that the failure was intermittent.

Repair a skipped final job without hiding failed tests

Consider this complete, deliberately failing miniature workflow. It needs no checkout or third-party action because it tests orchestration logic, not an application.

name: Interview dependency lab
on: workflow_dispatch
permissions: {}
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - run: exit 1
  gate:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - run: echo "checks complete"

Predict the result before reading on. The test job fails. Official dependency behavior: jobs that need a failed or skipped job are normally skipped unless an appropriate condition permits them to continue. Using jobs.

Adding an unconditional echo does not create a meaningful gate. The desired contract is: run the gate after a noncanceled test attempt, then fail the gate unless the test succeeded. Replace the gate with:

  gate:
    needs: test
    if: ${{ !cancelled() }}
    runs-on: ubuntu-latest
    steps:
      - name: Require a successful test job
        env:
          TEST_RESULT: ${{ needs.test.result }}
        run: |
          if [ "$TEST_RESULT" != "success" ]; then
            echo "Test job did not succeed"
            exit 1
          fi

Official expression detail: a default success condition applies unless a status function overrides it. always() can evaluate true even on cancellation; !cancelled() expresses a different policy. Expression reference.

For this exercise, a failed test should produce a failed gate, not a skipped one and not a green echo. A canceled workflow should not start this reporting work. If the policy requires cancellation reporting, design that separately rather than adding always() everywhere, including checkout and deployment.

The gate's shell was run locally with success, failure, skipped, and cancelled input strings. Only success returned zero. That validates the shell decision. GitHub still owns evaluation of the job condition and delivery of needs.test.result.

Explain a pending required check with no run

Here is a different original defect. Assume repository protection requires a check named unit-tests on every pull request:

name: Required test lab
on:
  pull_request:
    paths:
      - 'src/**'
permissions: {}
jobs:
  unit-tests:
    runs-on: ubuntu-latest
    steps:
      - run: exit 0

The pull request changes only README.md. No source path matches. The tiny successful command is irrelevant because the workflow never starts.

Official fact: when a required workflow is skipped by path or branch filtering, its associated required check can remain pending and block merging. That differs from an existing job skipped by its condition. Required-check troubleshooting.

The smallest repair for this miniature policy is to remove the path filter and run the required workflow on every relevant pull request. In a real repository, replace exit 0 with meaningful tests. Do not deploy a dummy green check as a substitute for the required contract.

If expensive tests genuinely apply only to certain files, design an always-created gate that verifies either completed tests or an explicitly justified nonapplicable result. Test documentation-only, source-only, and mixed changes. A skipped dependency must not accidentally make the gate green when tests were actually required.

Also inspect the exact configured check name. Renaming a job without updating protection rules can leave the old expected check missing. The debugging answer should connect the rule to the emitted check, not simply say “rerun CI.”

Fix a job permission override narrowly

The third original defect is a release job that uses GITHUB_TOKEN to create a release. Its relevant configuration looks like this; the write command is intentionally omitted:

permissions:
  contents: write
jobs:
  release:
    permissions:
      checks: write

The job-level map is not an additive request for one extra permission. Official rule: when you specify permissions, unspecified permissions are set to none at that scope. The release job therefore lacks the contents permission it needs. Assigning job permissions.

For this stated release-only requirement, set the job permission explicitly:

permissions: {}
jobs:
  release:
    permissions:
      contents: write

The exercise assumes a release target that does not introduce workflow-file changes relative to the default branch. Current release API documentation describes additional workflow authorization for that separate case; GITHUB_TOKEN cannot simply acquire that permission through this map. Check the resolved target commit as well as the endpoint. This is why adding write-all is neither a precise diagnosis nor a universal repair.

Keep checks: write only if the job also performs a justified Checks API write. The Create a release endpoint documents the relevant contents permission for fine-grained access. Establish the operation before choosing the scope.

Official authentication guidance: use the minimum GITHUB_TOKEN permissions needed, and inspect repository or organization settings rather than assuming every repository starts with the same defaults. Token authentication.

Do not diagnose every 403 as this YAML defect. Identify whether the step actually uses GITHUB_TOKEN, a GitHub App token, or another credential, and which repository it targets. Organization restrictions, event context, or the wrong repository can require a different repair. Log credential type and requested operation, never the token value.

Treat fork pull requests as a separate trust boundary

Official event behavior: fork pull-request workflows have restricted token and secret access under the documented default behavior, subject to relevant settings. pull_request_target runs in a different context and is not a drop-in permission upgrade for testing untrusted code. Workflow events.

A strong interview answer separates unprivileged code validation from privileged release work. Do not solve a fork failure by checking out attacker-controlled code in a job with write credentials or secrets. GitHub's secure-use guidance also explains risks from untrusted input and recommends stronger action pinning practices.

For an original follow-up, imagine a pull-request title is inserted directly into a shell command. Move the value into an environment variable and quote it as data. That addresses shell interpretation; it does not make an untrusted repository script safe to execute with credentials.

These examples intentionally use no third-party actions, so no action version is being advertised as current. For a real workflow, review the selected action's maintained version and pinning policy before copying a historical snippet from an interview answer.

Predict which overlapping run survives

Official concurrency behavior: with the default single pending slot, a group can have one running and one pending run; a new pending run replaces the older pending one. cancel-in-progress: true also requests cancellation of running work. Concurrency can scope a whole workflow or a particular job. Concurrency documentation.

Use this original workflow-level configuration for disposable validation:

concurrency:
  group: interview-ci-${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

Now change only the cancellation setting and predict this timeline. Assume the runs reach the same group in A, B, C order, A remains running, and C arrives before a pending B starts.

SettingB arrivesC arrives
cancel-in-progress false; default queueA runs; B waitsA runs; C replaces pending B
cancel-in-progress true; default queueCancellation requested for ALatest eligible run supersedes earlier work

Concurrency timeline contrasting pending replacement with cancellation of running work

Current documentation also describes queue: max, allowing up to 100 pending entries; it cannot be combined with cancel-in-progress: true. Do not present the default single-slot behavior as the only possible queue configuration. Waiting order is not a guarantee of commit-dispatch order. Concurrency documentation.

Choose the scope according to the resource you protect. Canceling an obsolete test run can save time. Canceling a deployment halfway through a database change can require recovery. A concurrency group is not a transaction or rollback mechanism.

Include the workflow name when unrelated workflows should not cancel each other. For a shared deployment target, intentionally sharing a group may be appropriate. Explain the collision boundary rather than memorizing one expression for every repository.

Show what your verification actually proves

The original complete workflows and reconstructed repairs were checked with actionlint 1.7.12. Its project documentation describes it as a static checker. Valid syntax does not prove that the workflow satisfies protection rules or can create a release in your repository.

For these exercises, static checking and local shell assertions are completed; no hosted runs, release writes, or repository protection changes were performed. The permission and scheduling tables describe expected platform behavior under the stated assumptions.

In a permitted test repository, the next checks would be a failed manual run, a successful manual run, a documentation-only pull request against the actual required-check rule, and overlapping commits. Inspect event, SHA, job conclusions, and logs. GitHub documents how to view and search workflow run logs.

Finish the explanation with one reason the repair might still be insufficient. For example, correcting the token map does not supply missing release assets; fixing the gate condition does not make a flaky application test reliable. This keeps the diagnosis tied to the defect you actually proved.

These are related delivery-system exercises, not a claim that an employer uses these exact Actions snippets.

PracHub questionPractice focus
Design a Dependency-Aware CI/CD PipelineMake dependency failures explicit.
Design a CI/CD pipeline with schedulerDefine run and queue behavior.
Design CI/CD Build CachingSeparate acceleration from correctness.
Design a CI/CD release notification serviceReport actual outcomes accurately.
Design a CI/CD system with live log streamingPreserve evidence for diagnosis.

Revisit the dependency-aware pipeline question and defend your gate policy for failed, skipped, and canceled upstream work.

Sources and Further Reading


Comments (0)