github-actions-checkout-path-outside-workspace
I diagnosed the issue and verified every claim against the live repositories. The solution is written to ~/solution.md.
Root cause (confirmed in actions/checkout@v4 source, src/input-helper.ts:36-51):
result.repositoryPath = core.getInput('path') || '.'
result.repositoryPath = path.resolve(githubWorkspacePath, result.repositoryPath)
if (!(result.repositoryPath + path.sep).startsWith(githubWorkspacePath + path.sep))
throw new Error(`Repository path '${result.repositoryPath}' is not under '${githubWorkspacePath}'`)
path.resolve(workspace, "/tmp/protocol") discards the workspace because the arg is absolute, then fails the containment guard. Four lines in sync-protocol.yml used /tmp/protocol (2× path:, 2× --protocol-dir), so both the check-schema-alignment and release jobs could never pass.
Sibling variant (confirmed in get-h3/h3 commit 7339893): in a workflow_call, an omitted repository: defaults to github.context.repo = the caller. The umbrella tree never appears at h3/, so defaults.run.working-directory: h3/integration/roundtrip dies on the first run: — a misleading working-directory error that is really a wrong-repo checkout.
35420892978: check-schema-alignment failed at step Run actions/checkout@v4, roundtrip skipped.35421835252: roundtrip / Python ↔ Go Round-Trip = success./tmp/protocol → REJECTED; .protocol-src → ACCEPTED.get-h3/protocol checkout: output byte-identical (sha256 df5a7033… before == after), so the diff check stays green.npx tsc --noEmit passes with .protocol-src present — the hidden dir is outside src/** include globs.The doc contains the exact diff for both fixes, detection commands, root-cause analysis with source citations, and a reusable checklist.
# Evidence - Problem class: github-actions-checkout-path-outside-workspace - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-19T05:25:45.167Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "CI job failed with a checkout error that names the runner workspace, not the workflow.\n\nSYMPTOM (get-h3/sdk-typescript, run 35420892978, workflow_dispatch, job check-schema-alignment):\nthe job's step \"Run actions/checkout@v4\" failed with\n\n Repository path '/tmp/protocol' is not under '~/work/sdk-typescript/sdk-typescript'\n\nand the downstream job `roundtrip` (gated on `needs: check-schema-alignment`) was SKIPPED, so the cross-language\nround-trip verification silently stopped running for a week. The checkout step is the FIRST failure, which makes the\nrun read like a workflow YAML/parsing problem or a permissions problem, not like a path rule.\n\nROOT CAUSE: actions/checkout requires its `path:` input to be RELATIVE to (and inside) $GITHUB_WORKSPACE. An absolute\npath is not rejected with a schema error - checkout attempt 1 succeeds into the workspace and then fails while\nresolving the destination, so the error surfaces as \"Repository path '<abs>' is not under '<workspace>'\". The workflow\nasked for `path: /tmp/protocol` in TWO jobs (check-schema-alignment and release) and pointed the generator at the same\nabsolute path (`--protocol-dir /tmp/protocol/schemas/v1`), so both were structurally incapable of passing.\n\nSIBLING VARIANT (same class, different face): a reusable workflow called from another repo (`uses:\nget-h3/h3/.github/workflows/roundtrip.yml@main`) that omits `repository:` on its first checkout checks out the\nCALLER's repository into the requested `path:`. The job-level `defaults.run.working-directory: h3/integration/roundtrip`\nthen does not exist, and the FIRST `run:` step dies with a misleading error, e.g.\n An error occurred trying to start process '/usr/bin/bash' with working directory\n '~/work/sdk-python/sdk-python/h3/integration/roundtrip'. No such file or directory\ni.e. a working-directory failure that is really a checkout-of-the-wrong-repo failure. The caller must name the\nrepository explicitly (`repository: <org>/<repo>`) in a reusable workflow; `${{ github.repository }}` resolves to the\nCALLER in a workflow_call, and omitting the key entirely defaults to the calling repo too.\n\nFIX (proven): make the side-checkout workspace-relative and keep it out of the package's compile/test scopes - a\nhidden directory works well because tsconfig `include` (src/**/*.ts) and vitest `include` (src/**/*.test.ts) then\ncannot see it:\n path: .protocol-src\n npx tsx scripts/generate-schemas.ts --protocol-dir .protocol-src/schemas/v1\nDETECTION before you debug anything else: `grep -n 'path: *[/~]' .github/workflows/*.yml` (any absolute path in a\ncheckout `path:`) and `git log -S'path: h3' -- .github/workflows/`. Read the failing STEP name from\n`gh api repos/<org>/<repo>/actions/runs/<id>/jobs --jq '.jobs[] | select(.conclusion==\"failure\") | [.name, (.steps[] | select(.conclusion==\"failure\") | .name)]'`\nbefore reading logs - \"Run actions/checkout@v4\" as the failing step means the path rule, not the YAML.\nVERIFICATION: after the fix, the push-triggered run of the same workflow showed the downstream roundtrip job execute\nand pass (run 35421835252, job \"roundtrip / Python <-> Go Round-Trip\" = success), and the generator run locally in\nthe CI form produced a byte-identical generated file (so the job's own diff check stayed green).", "environment": "GitHub Actions ubuntu-24.04 runners (runner 2.337.0), actions/checkout@v4; reproduced on get-h3/sdk-typescript (workflow_dispatch) and get-h3/sdk-python (workflow_call)", "language": "yaml", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "github-actions-checkout-path-outside-workspace", "provider": "openrouter", "solved_at": "2026-09-19T05:25:45.168Z", "version": ""}I diagnosed the issue and verified every claim against the live repositories. The solution is written to ~/solution.md.
Root cause (confirmed in actions/checkout@v4 source, src/input-helper.ts:36-51):
result.repositoryPath = core.getInput('path') || '.'
result.repositoryPath = path.resolve(githubWorkspacePath, result.repositoryPath)
if (!(result.repositoryPath + path.sep).startsWith(githubWorkspacePath + path.sep))
throw new Error(`Repository path '${result.repositoryPath}' is not under '${githubWorkspacePath}'`)
path.resolve(workspace, "/tmp/protocol") discards the workspace because the arg is absolute, then fails the containment guard. Four lines in sync-protocol.yml used /tmp/protocol (2× path:, 2× --protocol-dir), so both the check-schema-alignment and release jobs could never pass.
Sibling variant (confirmed in get-h3/h3 commit 7339893): in a workflow_call, an omitted repository: defaults to github.context.repo = the caller. The umbrella tree never appears at h3/, so defaults.run.working-directory: h3/integration/roundtrip dies on the first run: — a misleading working-directory error that is really a wrong-repo checkout.
35420892978: check-schema-alignment failed at step Run actions/checkout@v4, roundtrip skipped.35421835252: roundtrip / Python ↔ Go Round-Trip = success./tmp/protocol → REJECTED; .protocol-src → ACCEPTED.get-h3/protocol checkout: output byte-identical (sha256 df5a7033… before == after), so the diff check stays green.npx tsc --noEmit passes with .protocol-src present — the hidden dir is outside src/** include globs.The doc contains the exact diff for both fixes, detection commands, root-cause analysis with source citations, and a reusable checklist.
# Evidence - Problem class: github-actions-checkout-path-outside-workspace - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-19T05:25:45.167Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "CI job failed with a checkout error that names the runner workspace, not the workflow.\n\nSYMPTOM (get-h3/sdk-typescript, run 35420892978, workflow_dispatch, job check-schema-alignment):\nthe job's step \"Run actions/checkout@v4\" failed with\n\n Repository path '/tmp/protocol' is not under '~/work/sdk-typescript/sdk-typescript'\n\nand the downstream job `roundtrip` (gated on `needs: check-schema-alignment`) was SKIPPED, so the cross-language\nround-trip verification silently stopped running for a week. The checkout step is the FIRST failure, which makes the\nrun read like a workflow YAML/parsing problem or a permissions problem, not like a path rule.\n\nROOT CAUSE: actions/checkout requires its `path:` input to be RELATIVE to (and inside) $GITHUB_WORKSPACE. An absolute\npath is not rejected with a schema error - checkout attempt 1 succeeds into the workspace and then fails while\nresolving the destination, so the error surfaces as \"Repository path '<abs>' is not under '<workspace>'\". The workflow\nasked for `path: /tmp/protocol` in TWO jobs (check-schema-alignment and release) and pointed the generator at the same\nabsolute path (`--protocol-dir /tmp/protocol/schemas/v1`), so both were structurally incapable of passing.\n\nSIBLING VARIANT (same class, different face): a reusable workflow called from another repo (`uses:\nget-h3/h3/.github/workflows/roundtrip.yml@main`) that omits `repository:` on its first checkout checks out the\nCALLER's repository into the requested `path:`. The job-level `defaults.run.working-directory: h3/integration/roundtrip`\nthen does not exist, and the FIRST `run:` step dies with a misleading error, e.g.\n An error occurred trying to start process '/usr/bin/bash' with working directory\n '~/work/sdk-python/sdk-python/h3/integration/roundtrip'. No such file or directory\ni.e. a working-directory failure that is really a checkout-of-the-wrong-repo failure. The caller must name the\nrepository explicitly (`repository: <org>/<repo>`) in a reusable workflow; `${{ github.repository }}` resolves to the\nCALLER in a workflow_call, and omitting the key entirely defaults to the calling repo too.\n\nFIX (proven): make the side-checkout workspace-relative and keep it out of the package's compile/test scopes - a\nhidden directory works well because tsconfig `include` (src/**/*.ts) and vitest `include` (src/**/*.test.ts) then\ncannot see it:\n path: .protocol-src\n npx tsx scripts/generate-schemas.ts --protocol-dir .protocol-src/schemas/v1\nDETECTION before you debug anything else: `grep -n 'path: *[/~]' .github/workflows/*.yml` (any absolute path in a\ncheckout `path:`) and `git log -S'path: h3' -- .github/workflows/`. Read the failing STEP name from\n`gh api repos/<org>/<repo>/actions/runs/<id>/jobs --jq '.jobs[] | select(.conclusion==\"failure\") | [.name, (.steps[] | select(.conclusion==\"failure\") | .name)]'`\nbefore reading logs - \"Run actions/checkout@v4\" as the failing step means the path rule, not the YAML.\nVERIFICATION: after the fix, the push-triggered run of the same workflow showed the downstream roundtrip job execute\nand pass (run 35421835252, job \"roundtrip / Python <-> Go Round-Trip\" = success), and the generator run locally in\nthe CI form produced a byte-identical generated file (so the job's own diff check stayed green).", "environment": "GitHub Actions ubuntu-24.04 runners (runner 2.337.0), actions/checkout@v4; reproduced on get-h3/sdk-typescript (workflow_dispatch) and get-h3/sdk-python (workflow_call)", "language": "yaml", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "github-actions-checkout-path-outside-workspace", "provider": "openrouter", "solved_at": "2026-09-19T05:25:45.168Z", "version": ""}