Skip to content

copilot/arc-dind: XDG_CONFIG_HOME resolves to unwritable /home/runner #47802

Description

@r-garcia-de-oliveira

Summary

Under runner.topology: arc-dind with the copilot engine, gh-aw exports
XDG_CONFIG_HOME="$HOME", but at the point that export runs $HOME is the unwritable
passwd home /home/runner
(root-owned) — not the writable arc-dind home
${RUNNER_TEMP}/gh-aw/home that gh-aw itself sets HOME to. Any tool that resolves its config
dir from XDG_CONFIG_HOME (Flutter's Config/tool_state, and others) then tries to write into
/home/runner and fails with a permission error:

Flutter failed to write to a file at "/home/runner/tool_state".
The flutter tool cannot access the file or directory.
Please ensure that the SDK and/or project is installed in a location that has read/write permissions for the current user.

Impact

  • Breaks any XDG-respecting tool run by the agent under arc-dind (observed with the Flutter SDK,
    but it affects anything using XDG_CONFIG_HOME).
  • HOME itself is correctly the writable ${RUNNER_TEMP}/gh-aw/home, so this is subtle: only
    the XDG-derived config path is poisoned, which masks the root cause.
  • Setting XDG_CONFIG_HOME via the workflow engine.env does not help — this
    export XDG_CONFIG_HOME="$HOME" runs later and overwrites it. The only reliable consumer-side
    workaround is to override HOME to a writable literal in engine.env so $HOME is writable
    when the export evaluates.

Environment

Diagnostic (from inside the sandbox)

A trivial agent step dumping the environment shows the split:

HOME=/home/runner/_work/_temp/gh-aw/home          # correct, writable
XDG_CONFIG_HOME=/home/runner                       # WRONG — unwritable
id -> uid=1001(runner) gid=1001(runner)
getent passwd 1001 -> runner:x:1001:1001:...:/home/runner:/bin/bash   # passwd home = /home/runner
ls -ld /home/runner                       -> drwxr-xr-x root root ...   writable: NO
ls -ld /home/runner/_work/_temp/gh-aw/home-> drwxr-xr-x runner runner   writable: yes

So HOME was correctly overridden to the writable arc-dind home, but XDG_CONFIG_HOME captured
the unwritable passwd home /home/runner.

Root cause

Permalinks against main:

  • pkg/workflow/copilot_engine_execution.go#L115buildCopilotMCPConfigExport always emits:
    b.WriteString("export XDG_CONFIG_HOME=\"$HOME\"\n")
    (doc comment: "XDG_CONFIG_HOME=$HOME (Copilot CLI resolves its config dir from this)").
  • Under arc-dind, gh-aw already knows the writable home path — it's the compiler constant
    awfArcDindHomePathExpr = "${RUNNER_TEMP}/gh-aw/home" (pkg/workflow/awf_helpers.go#L47), and
    rewriteArcDindEngineCommand (awf_helpers.go#L142) prepends export HOME=${RUNNER_TEMP}/gh-aw/home
    to the engine command.
  • But XDG_CONFIG_HOME is indirected through $HOME, and at exec time inside the chroot $HOME
    resolves to the synthesized passwd home /home/runner (root-owned, unwritable) rather than the
    writable arc-dind home. Result: XDG_CONFIG_HOME=/home/runner.

Net: HOME is fixed up for arc-dind, but the XDG export that "follows" it captures the wrong,
unwritable home.

Suggested fix

When compiling for runner.topology: arc-dind, export XDG_CONFIG_HOME to the same writable
constant used for HOME, instead of indirecting through $HOME:

// arc-dind: XDG must point at the writable arc-dind home, not the (unwritable) passwd $HOME
b.WriteString(fmt.Sprintf("export XDG_CONFIG_HOME=%q\n", awfArcDindHomePathExpr))

(Non-arc-dind keeps export XDG_CONFIG_HOME="$HOME".) Alternatively, ensure the HOME override
is in effect in the same shell before the XDG export evaluates.

Notes

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions