Summary
fixArtifactPermissionsForRootless() is failing for all directories during post-agent cleanup, causing the Copilot CLI to exit with code 1. The repair docker run command exits 1 but stderr is not captured, making the root cause opaque.
Observed in: https://github.com/github/gh-aw-mcpg/actions/runs/29042841176 (Large Payload Tester)
Symptom
After the agent completes successfully and containers are stopped, the cleanup phase fails:
[WARN] Rootless artifact permission repair failed for /tmp/gh-aw/sandbox/firewall/logs (exit 1)
[WARN] Rootless artifact permission repair failed for /tmp/gh-aw/sandbox/firewall/audit (exit 1)
[WARN] Rootless artifact permission repair failed for /tmp/awf-1783623821084-chroot-home (exit 1)
[WARN] Failed to remove chroot home directory after permission repair: Error: EACCES: permission denied, unlink '/tmp/awf-1783623821084-chroot-home/.aws/config'
[WARN] Command completed with exit code: 1
Process exiting with code: 1
Each repair attempt takes ~30s (suggesting Docker overhead or timeout), and the final EACCES on .aws/config causes the process to exit non-zero despite the agent task completing successfully.
Root cause analysis
Problem 1: No stderr logging from repair container
In artifact-permissions.ts (lines 50–83), the docker run for permission repair uses execa.sync with reject: false, but does not capture or log stderr. When the repair exits with code 1, we only see the exit code — not why it failed.
Possible failure reasons (all invisible without stderr):
- Docker daemon issue (image not found, daemon busy)
- Container startup failure (the agent image's entrypoint may not support bare
sh -c properly)
- Volume mount failure (path doesn't exist from Docker daemon's perspective)
- The
awf-net network was still "in use" at cleanup time — could indicate a Docker daemon state issue
Problem 2: Repair failure escalates to fatal exit code
The flow in artifact-preservation.ts:
preserveAndCleanupArtifacts() calls fixArtifactPermissionsForRootless() for logs/audit/session-state (line 214) — failures are warnings only ✅
removeWorkDirectories() tries fs.rmSync(chrootHomeDir) — hits EACCES on root-owned files (line 234)
- Catches
EACCES, calls fixArtifactPermissionsForRootless() again for chroot-home (line 241) — also fails
- Retries
fs.rmSync() — fails again with EACCES (line 249)
- Logs warning: "Failed to remove chroot home directory after permission repair" (line 251)
This warning propagates as exit code 1 from the Copilot CLI, even though:
- The agent task completed successfully
- The
Post Setup Scripts step successfully cleaned up the chroot-home directory afterward (Cleaned up 1 /tmp/awf-*-chroot-home directory)
Problem 3: Redundant cleanup
The Post Setup Scripts step (compiled by gh-aw) has its own sudo cleanup of chroot-home directories, and it succeeded. AWF's Node.js rimrafSync attempt is redundant when running under gh-aw, and its EACCES failure is what causes the non-zero exit.
Proposed fixes
Fix 1: Capture and log stderr from repair container (diagnostic)
// artifact-permissions.ts line 81
if (typeof result.exitCode === 'number' && result.exitCode !== 0) {
logger.warn(
`Rootless artifact permission repair failed for ${dir} (exit ${result.exitCode})` +
(result.stderr ? `: ${result.stderr}` : ''),
);
}
Fix 2: Make chroot-home removal failure non-fatal
The removeWorkDirectories() function should not cause the process to exit non-zero when it can't remove the chroot-home directory. The files will be cleaned up by the post-step (install_copilot_cli.sh cleanup) or by the runner infrastructure. Change the catch at line 250 to swallow the error:
} catch (retryError) {
// Non-fatal: chroot-home will be cleaned by post-step or runner infra.
logger.debug('Could not remove chroot home directory after permission repair — will be cleaned by post-step');
}
Fix 3: Investigate the 30s repair latency
Each docker run --rm --pull never --network none should be nearly instant for a chown+chmod. The ~30s per call suggests something is wrong at the Docker level. With stderr captured (Fix 1), we can diagnose whether this is a pull attempt, image issue, or daemon state problem.
Environment
- AWF v0.27.27
- Runner: GitHub-hosted (ubuntu-latest)
- Network isolation mode enabled
- Note:
awf-net reported "Resource is still in use" during docker compose down, possibly related
Files
src/artifact-permissions.ts — repair container logic (no stderr capture)
src/artifact-preservation.ts — removeWorkDirectories() where EACCES escalates to fatal
src/container-stop.ts — pre-shutdown squid log chmod (works correctly)
Summary
fixArtifactPermissionsForRootless()is failing for all directories during post-agent cleanup, causing the Copilot CLI to exit with code 1. The repairdocker runcommand exits 1 but stderr is not captured, making the root cause opaque.Observed in: https://github.com/github/gh-aw-mcpg/actions/runs/29042841176 (Large Payload Tester)
Symptom
After the agent completes successfully and containers are stopped, the cleanup phase fails:
Each repair attempt takes ~30s (suggesting Docker overhead or timeout), and the final
EACCESon.aws/configcauses the process to exit non-zero despite the agent task completing successfully.Root cause analysis
Problem 1: No stderr logging from repair container
In
artifact-permissions.ts(lines 50–83), thedocker runfor permission repair usesexeca.syncwithreject: false, but does not capture or log stderr. When the repair exits with code 1, we only see the exit code — not why it failed.Possible failure reasons (all invisible without stderr):
sh -cproperly)awf-netnetwork was still "in use" at cleanup time — could indicate a Docker daemon state issueProblem 2: Repair failure escalates to fatal exit code
The flow in
artifact-preservation.ts:preserveAndCleanupArtifacts()callsfixArtifactPermissionsForRootless()for logs/audit/session-state (line 214) — failures are warnings only ✅removeWorkDirectories()triesfs.rmSync(chrootHomeDir)— hitsEACCESon root-owned files (line 234)EACCES, callsfixArtifactPermissionsForRootless()again for chroot-home (line 241) — also failsfs.rmSync()— fails again withEACCES(line 249)This warning propagates as exit code 1 from the Copilot CLI, even though:
Post Setup Scriptsstep successfully cleaned up the chroot-home directory afterward (Cleaned up 1 /tmp/awf-*-chroot-home directory)Problem 3: Redundant cleanup
The
Post Setup Scriptsstep (compiled by gh-aw) has its ownsudocleanup of chroot-home directories, and it succeeded. AWF's Node.jsrimrafSyncattempt is redundant when running under gh-aw, and itsEACCESfailure is what causes the non-zero exit.Proposed fixes
Fix 1: Capture and log stderr from repair container (diagnostic)
Fix 2: Make chroot-home removal failure non-fatal
The
removeWorkDirectories()function should not cause the process to exit non-zero when it can't remove the chroot-home directory. The files will be cleaned up by the post-step (install_copilot_cli.shcleanup) or by the runner infrastructure. Change thecatchat line 250 to swallow the error:Fix 3: Investigate the 30s repair latency
Each
docker run --rm --pull never --network noneshould be nearly instant for achown+chmod. The ~30s per call suggests something is wrong at the Docker level. With stderr captured (Fix 1), we can diagnose whether this is a pull attempt, image issue, or daemon state problem.Environment
awf-netreported "Resource is still in use" duringdocker compose down, possibly relatedFiles
src/artifact-permissions.ts— repair container logic (no stderr capture)src/artifact-preservation.ts—removeWorkDirectories()where EACCES escalates to fatalsrc/container-stop.ts— pre-shutdown squid log chmod (works correctly)