apple / apple/container

[Bug]: `container system start` discards launchctl bootstrap failures, yielding a misleading XPC error in non-login (CI) sessions

Open
#2,008 6 comments 0 reactions 0 assignees View on GitHub
stability
Dominant language
Swift
Stars
49.9k
Forks
1.8k
Avg merge
1d 20h
Merged PRs (30d)
22

Description

### I have done the following

- [x] I have searched the existing issues
- [ ] If possible, I've reproduced the issue using the 'main' branch of this project

### Steps to reproduce

On a session whose launchd context is not a logged-in user session — a GitHub Actions `macos-26` runner reproduces this deterministically:

```sh
sudo installer -pkg container-installer-unsigned.pkg -target /
sudo container system start --enable-kernel-install
```

Output (instant, no retry helps — 15 consecutive attempts fail identically):

```
Launching container-apiserver...
Testing access to container-apiserver...
Error: internalError: "failed to get a response from apiserver: interrupted: "XPC connection error: Connection invalid""
Ensure container system service has been started with `container system start`.
```

`log show --predicate 'subsystem == "com.apple.container"'` over the failure window is **completely empty** — `container-apiserver` never launched at all.

### Problem description

Two layered problems:

**1. The launchctl bootstrap failure is silently discarded.** `ServiceManager.register` (Sources/ContainerPlugin/ServiceManager.swift) pipes launchctl's stdout/stderr to `/dev/null` *and* ignores its exit status:

```swift
public static func register(plistPath: String) throws {
let domain = try Self.getDomainString()
_ = try runLaunchctlCommand(args: ["bootstrap", domain, plistPath])
}
```

When the bootstrap fails, `SystemStart` proceeds to "Testing access to container-apiserver..." and dies with a generic XPC error that points the user back at the very command that just failed ("Ensure container system service has been started with `container system start`"). The actual launchctl error — which names the domain and the real reason — is never shown. This cost us several debugging round-trips chasing the XPC layer before reading the source revealed the ping was doomed before it ran.

**2. The computed domain is wrong for sudo-in-a-background-session.** `getDomainString()` maps `launchctl managername` + `getuid()`: Aqua → `gui/`, Background → `user/`. Under `sudo` in a CI runner's session, that computes `user/0`/`gui/0`, which can't be bootstrapped into — so registration always fails there. This is the same class of problem as #1514, but note the service plist already includes `System` in `LimitLoadToSessionType`, so the system domain looks intended to be supported.

**Suggested behavior:**
- At minimum: `register()` should check launchctl's exit status and surface its stderr in the thrown error. That one change would have made the root cause obvious from the first failure.
- Ideally: when running as root (euid 0) outside an Aqua session, bootstrap into the `system` domain. We verified this works end to end as a workaround — after `container system start` writes the plist, running `sudo launchctl bootstrap system ` and re-running `container system start` brings everything up (apiserver, machine-apiserver, plugins, kernel install), and Mach lookups chain gui → user → system so the service is reachable from any session. Working example: https://github.com/cap10morgan/whalebridge/blob/7de7456/.github/workflows/ci.yml (the "Start apple/container services" step; also being adopted by socktainer/socktainer#325).

### Environment

- OS: macOS 26 (GitHub Actions `macos-26` runner image, Apple silicon)
- Xcode: Xcode 26 (runner default)
- Container: container CLI version 1.1.0 (release pkg)

### Code of Conduct

- [x] I agree to follow this project's Code of Conduct

Contributor guide

Open the contributing guide

Research direction

Start in Sources/ContainerPlugin/ServiceManager.swift, especially ServiceManager.register and getDomainString, and trace how SystemStart handles registration before testing container-apiserver. Reproduce the sudo launchctl failure in a non-login macOS CI session, then verify that bootstrap failures expose launchctl's error and that the intended root/non-Aqua behavior is covered by tests or an end-to-end start run.

Written by the indexing model from the issue text.

Assessment

Tech stack
macos, shell, swift
Domain
cli, operating-systems
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.