Repository navigation
fix(install): wait for launchd service to fully unload before bootstrap - #916
Open
edwardtoday wants to merge 1 commit into
Open
edwardtoday wants to merge 1 commit into
edwardtoday wants to merge 1 commit into
Conversation
launchdUserStopAndWait decided the service had stopped by looking for `state = running` only. After bootout, launchd keeps the service in `state = SIGTERMed` while the daemon gracefully shuts down its headless instances, so the wait loop returned immediately and bootstrap was issued against a label that was still registered. macOS reports that as "Bootstrap failed: 5: Input/output error", which is not matched by isLaunchdAlreadyLoadedErr (it only looks for "already loaded"). The local upgrade therefore failed, and the rollback hit the same race, leaving the service unloaded until it was bootstrapped by hand. Wait until `launchctl print` reports the service as missing instead. Fixes kxn#914
This was referenced Sep 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #914.
问题
macOS 上本地升级失败,接着回滚也失败,最终把服务留在未加载状态(
launchctl报 "Could not find service"),也就是 daemon 在人工 bootstrap 之前一直不可用:根因:
launchdUserStopAndWait先bootout,然后等待launchdUserIsRunning,而后者只认state = running。但bootout之后服务不会立刻消失,而是先进入state = SIGTERMed(daemon 正在优雅关闭全部 headless 实例)。该状态被判为「已停止」,于是没有发生任何等待,紧接着就对仍然注册着的 label 执行bootstrap。macOS 对此返回Bootstrap failed: 5: Input/output error,而isLaunchdAlreadyLoadedErr匹配不到它(该函数只找"already loaded"),于是升级被判定为失败,回滚又撞上同一个竞态。直接复现:
另外需要注意:对已经加载的服务重复执行
bootstrap,返回的同样是这个Input/output error,而不是"already loaded",所以不能仅凭错误串判断。修复
新增
launchdUserIsUnloaded:只有launchctl print报 missing(服务已从 domain 完全卸载)才视为停止,并让launchdUserStopAndWait使用它。原有的超时路径(terminate 后上报失败)保持不变。测试
TestLaunchdUserStopAndWaitWaitsForFullUnload通过 mock launchctl,让服务先两次返回state = SIGTERMed,之后才转为 missing。在 master 上它以print calls=1(等待立即返回)失败;修复后通过。已用
go test ./internal/app/install/验证;完整的upgrade-local.sh流程现在能跑完prepared → switching → observing → committed,切换后healthz返回 200。备注
CI 里的
scripts/check/no-local-paths.sh在干净的master上同样失败(internal/core/orchestrator/service_target_picker_polluted_workspace_test.go中的/home/qagent/...不在 allowlist 内),与本 PR 无关。