[Bug][macOS] Killing a blocked `security` Keychain read after 5s crashes securityd and causes an endless login-keychain prompt loop

Abierto
#8,161 2 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

@lidge-jun ya está trabajando en esto.

Desde el 8/11/2026.

  • #8327 de @gaurav0107 — abierto
  • #8337 de @lidge-jun — abierto

Evaluación

Dificultad
4/5
Tiempo estimado
3-5 días
Aptitud para principiantes
55/100
Tipo de issue
Error
Claridad
Bastante claro
Estado de actividad
Activo
Stack tecnológico
macos, typescript

Línea de trabajo

Start with src/oauth/local-token-detect.ts and src/claude/auth-detect.ts, then trace how their security child processes handle the 5 s timeout. Reproduce with a locked login keychain and check whether the reads trigger repeated prompts or securityd crashes. Done when these reads avoid the reported crash loop and retain their existing fallback behavior.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

account-pool bug platform service
Client or integration

Claude Code

Area

Authentication and account pool

Summary

On macOS 27.0, the Claude Code Keychain reads in src/oauth/local-token-detect.ts (readClaudeKeychain, readClaudeKeychainAsync) and the probe in src/claude/auth-detect.ts (keychainProbe) run /usr/bin/security with a 5 s timeout. When the login keychain is locked, security blocks on the SecurityAgent unlock prompt, and the timeout kills it while the prompt is still open.

Killing a security process while its SecurityAgent query is pending hits a race in Apple's securityd: it logs Probable bug: error destroying Mutex: 16 / UNIX error exception: 16 and aborts (SIGABRT, std::terminate). When securityd restarts, the login keychain is locked again. The next request reads the keychain, gets killed after 5 s, and crashes securityd again, so the user sees an endless "security wants to use the login keychain" password prompt.

I expected OpenCodex not to kill a security that is blocked on a prompt, or to skip the read when the login keychain is locked.

The securityd race itself is Apple's bug, but other tools have hit the same trigger and stopped the loop by no longer killing blocked security calls:

Reproduction

Intermittent, because it needs the login keychain to be locked first (one securityd crash, or a keychain whose password doesn't match the login password).

  1. Run ocx as the launchd service with Claude Code signed in through the macOS Keychain.
  2. Get the login keychain locked (for example after one securityd crash).
  3. Send Claude requests through the proxy. Each read spawns security find-generic-password -s "Claude Code-credentials" -w, which blocks on the unlock prompt and is killed after 5 s.
  4. securityd crashes, the keychain locks again, and the prompts keep coming back.

What I observed: 62 security runs in 7 minutes, 16 of them with no normal exit, and 4 securityd crash reports (/Library/Logs/DiagnosticReports/securityd-*.ips), all with the same faulting MachServer stack.

Version

2.97.0 (2.96.0 behaves the same)

Operating system

macOS 27.0 (26A428), Apple Silicon

Provider and model

Claude (claude-cli adapter, Keychain credentials)

Logs or error output
securityd: [com.apple.securityd:SecurityAgentXPCQuery] SecurityAgentXPCQuery(...) dying
securityd: [com.apple.securityd:SecError] Probable bug: error destroying Mutex: 16
securityd: [com.apple.securityd:security_exception] UNIX error exception: 16
crash: EXC_CRASH (SIGABRT), abort() called, faulting thread "MachServer", std::terminate
Screenshots and supporting files

The prompt that keeps coming back: "security wants to use the 'login' keychain. Enter the keychain password."

Redacted configuration

Default service install; nothing relevant in the config.

Possible fix

The workaround I'm running locally checks the login keychain lock state without showing any UI (SecKeychainOpen + SecKeychainGetStatus through bun:ffi) and skips the security call when it is locked. The read returns null (or "unknown" for the probe) and falls back as it already does on a timeout. If anything fails, it returns "not locked", so the behaviour stays the same as now.

export function loginKeychainLocked(path = join(homedir(), "Library", "Keychains", "login.keychain-db")): boolean {
  if (process.platform !== "darwin") return false;
  // dlopen Security.framework: SecKeychainOpen, SecKeychainGetStatus; CoreFoundation: CFRelease
  // return (status & kSecUnlockStateStatus) === 0; any error -> false
}

Then call it at the start of readClaudeKeychain, readClaudeKeychainAsync and keychainProbe. Another option is to stop killing a blocked child: resolve null after 5 s but let the child exit on its own, and share a single in-flight read.

readMuseKeychain already waits 120 s for an interactive login, so I left it alone.

Checks
  • I searched existing issues and documentation.
  • I removed secrets, tokens, account details, request credentials, and personal data.
Lenguaje dominante
TypeScript
Estrellas
16.9k
Forks
1.3k
Merge medio
23 h
PR fusionados (30 d)
687

Preparar el entorno

Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de lidge-jun/opencodex

Todos los issues de lidge-jun/opencodex

Issues similares

Más issues de TypeScript

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.