GiteraGitera Docs

Debug sessions

Pause a failed GitHub Actions job for up to 15 minutes and debug it from a secure terminal in Gitera — no tunnels, no open ports, secrets masked.

Open in Gitera
Available onBasicProEnterprise

Stop pushing test ci 1, test ci 2, test ci 3 to find out why a command fails on the runner. With debug sessions, a failed job pauses and you get a terminal on the runner, inside the Gitera dashboard. Try your fix there, then export it as a patch.

How it works

  1. Your workflow runs the gitera-io/gitera-debug-action@v1 step after a failure, or on a re-run you armed.
  2. The runner proves who it is with the job's GitHub OIDC token and connects outbound to Gitera over HTTPS.
  3. Gitera checks your debug policy and plan, then opens a session.
  4. You open the Debug Runner Session window and type commands. The job waits up to 15 minutes, then continues.

The runner opens no ports and uses no third-party tunnel. No Gitera secret is stored in your repository.

Add the step

permissions:
  contents: read
  id-token: write # required: lets the runner prove its repo, run and branch

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm test

      - uses: gitera-io/gitera-debug-action@v1
        if: ${{ !cancelled() && (failure() || github.run_attempt > 1) }}
        with:
          mode: ${{ job.status == 'failure' && 'failure' || 'armed' }}
          secrets: ${{ toJSON(secrets) }}
InputDefaultWhat it does
modefailurefailure opens a session whenever the step runs. armed opens one only when you armed it from Gitera or by commenting /gitera debug on the pull request. See Choose a mode.
secrets{}Pass ${{ toJSON(secrets) }}. Every value is masked in the terminal and in fix diffs, however short, including base64 and URL-encoded forms. Output that could be the start of a secret is held until the next output shows it is not.
timeout-minutes15How long the job may stay paused, 1–15 minutes.

The step always exits successfully, so it never changes the job result. Linux and macOS runners are supported; Windows runners skip the step. The runner needs node, git and python3, which GitHub-hosted runners already have. On self-hosted macOS runners python3 is required; on Linux without it, the step falls back to util-linux script with a fixed terminal size.

Each matrix leg is its own job, so every failing leg can open its own session.

Choose a mode

mode decides when the step may pause the job. A session is armed when someone asked for it ahead of time: with Debug on re-run in Gitera, or with a /gitera debug comment on the pull request. Gitera then re-runs the run, and the armed session waits 60 minutes for a runner to claim it.

ModeStep runs, nothing armedStep runs, a session is armed for this run
failureOpens a new session and pausesTakes over the armed session and pauses
armedExits at once; the job continuesTakes over the armed session and pauses

The snippet above uses both. Its if: runs the step after a failure or on any re-run, and its mode picks failure when the job failed and armed otherwise:

CaseStep runs?ModeResult
A step failedYesfailurePauses and opens a session
First run, everything passedNo—Nothing happens
Re-run you armed, and the job passes this timeYesarmedPauses, so you can still inspect the runner
Someone clicked Re-run without armingYesarmedExits at once, no pause

armed exists because a flaky job often passes on the re-run. With failure alone the step would not run, and the armed session would wait for nothing. With armed, a passing re-run still pauses, but only when you asked for it.

failure pauses on every failure, even when nobody joins. Each pause holds the runner for up to timeout-minutes of billed time. To pause only when you ask, set mode: armed and use if: ${{ !cancelled() && github.run_attempt > 1 }}.

Start a session

  • Automatically on failure. The job pauses at the step. The run summary and a job annotation link to the session.
  • From Gitera. On a workflow page, open Recent runs and click Debug on re-run on a failed run. Gitera arms a session, re-runs the failed jobs, and opens the session in a new tab.
  • From a pull request. Write /gitera debug as a comment on the pull request in GitHub, alone on the first line. See Debug from a pull request comment.
  • From GitHub. With the Action Link extension, run pages that have a live session show a Debug Runner Session button.

An armed session waits 60 minutes for the re-run to reach the debug step.

Debug from a pull request comment

/gitera debug is not a CLI command. It is a comment you post on the pull request in GitHub, like /rca. The Gitera GitHub App reads it.

/gitera debug
/gitera debug 18342095127

The command must be alone on the first line of the comment. Text after the first line is ignored.

  1. Gitera finds the run. Without a run ID, it takes the latest failed run for the PR's latest commit. With a run ID, it uses that run.
  2. Gitera checks that you may debug it: write access to the repository, or authorship of this pull request.
  3. Gitera arms a session and re-runs the failed jobs of that run.
  4. Gitera replies on the PR with a link to the session.
  5. When the re-run reaches the debug step, the terminal opens. The arm waits 60 minutes.

The workflow must run the debug step on re-runs (github.run_attempt > 1) with mode: armed, like the snippet in Add the step. Without it, the re-run never pauses and the armed session expires.

Reply on the PRWhy
No failed workflow run found for the latest commitNothing failed on the PR's latest commit. Pass a run ID, or push and let a run fail first.
Gitera could not start a debug session: …You lack write access and did not open the PR, the branch is protected, the run is still in progress, or a session is already armed for the run.
No reply at allGitera is not installed on the repository, the organization is on the Free plan, or the comment was edited instead of posted new.

Debug Sessions page

Monitoring → Debug Sessions (/debug) lists the selected organization's recent sessions. A session belongs to the repository owner; when another of your organizations has a live session, a notice offers Show all organizations. Cards count sessions that are Live, Armed and Refused. Each row shows the repository, run, job, branch, who started it, the trigger and the time left. Open terminal opens a live or armed session in a new tab. Refused rows show the reason. The list refreshes every 10 seconds while a session is live or armed.

Who can join

You must be signed in to Gitera with GitHub and be an active member of the organization (disabled members are refused). On top of that, you need write access to the repository, or you must be the author of the pull request the run belongs to. Everyone else is refused, including read-only members.

An organization can have 5 live sessions at a time.

Protected branches

Sessions are refused on protected branches. By default these are main, master, release/* and release/**, so nested branches such as release/2026/hotfix are refused too.

For pull_request_target runs, Gitera checks the base branch the job runs on, not the PR's head branch.

To change the list, add the Interactive debug policy (block-interactive-debug) on the Policies page and set protectedBranches. Patterns support * (one path segment) and ** (any depth).

The same policy flags workflows where the debug action can run on a protected branch. A step passes only when its triggers cannot reach any protected branch, or when its job and step if: conditions exclude each one with &&-joined terms such as github.ref != 'refs/heads/main' or !startsWith(github.ref, 'refs/heads/release/'). A condition with || does not count as a guard.

Generate Fix Diff

Click Generate Fix Diff in the session window. Gitera shows git diff HEAD from the runner's workspace, plus any new untracked files. Copy it or Download .patch, then apply it locally:

git apply gitera-fix-<run-id>.patch

Nothing is committed or pushed for you. Diffs over 1 MB are cut and marked as such.

Audit log

Gitera records every session event: arms, runner connects, joins, refused joins, submitted command lines, diff exports and ends. Typed keystrokes are recorded per submitted line, not per key. Credential-shaped values in a submitted line (TOKEN=..., --password ..., bearer tokens, GitHub tokens) are redacted before storage. The audit log is not readable from the dashboard or the API by organization members.

The debug shell does not get the job's OIDC request credentials, so a session user cannot mint new OIDC tokens for the job.

Limits

LimitValue
Pause per session15 minutes
Arm window60 minutes
Live sessions per organization5
Fix diff size1 MB

A paused job keeps its runner busy, so it bills runner minutes while you debug.

Troubleshooting

SymptomFix
Log says No OIDC tokenAdd permissions: id-token: write to the workflow or job.
Interactive debug sessions are disabled on protected branchDebug on a feature branch, or change the block-interactive-debug policy.
Only repo write collaborators and the PR author can joinAsk a maintainer for write access, or debug from your own PR.
Window says Waiting for runnerThe re-run has not reached the debug step yet, or the step uses mode: failure and the job passed.

On this page