Repository navigation
auth login/status/doctor hang forever (no timeout) when keyring's D-Bus handshake stalls; BASECAMP_NO_KEYRING=1 works but is undocumented #800
Description
Summary
Any basecamp command that touches stored credentials (auth login, auth status, doctor, projects list, etc.) hangs forever, with no error and no timeout, on a Linux desktop where a D-Bus session bus and gnome-keyring-daemon are both present and working normally. Root cause: the github.com/zalando/go-keyring Secret Service (D-Bus) backend's low-level D-Bus auth handshake never completes, and nothing times it out.
Environment
basecamp version 0.11.0- Linux (Omarchy/Hyprland desktop),
gnome-keyring-daemonrunning (pkcs11,secretscomponents), registered on the session bus asorg.freedesktop.secrets/org.gnome.keyring dbus-send --session ... org.freedesktop.DBus.ListNamesworks fine and returns immediately — the bus itself and other D-Bus clients are healthy- System clock is NTP-synced
Reproduction
$ basecamp auth status --json
# hangs indefinitely, no output, no timeout
Same hang on basecamp doctor --json, basecamp auth login (both default browser flow and --device-code/--remote), and any data command like basecamp projects list once credentials need to be read. --version and --help return instantly (they don't touch the credential store), which is what pointed us at credential storage rather than the network as the culprit — a plain curl -v https://3.basecamp.com / https://launchpad.37signals.com completes in well under 200ms the whole time this is hanging, ruling out a network/proxy issue.
Diagnosis
Sent SIGQUIT to the hung process to get a goroutine dump (Go's default behavior — dumps all stacks and exits). The main goroutine is parked here even during a device-code login after the OAuth exchange has already succeeded (Authorization.GetInfo and People.Me both completed):
goroutine 1 [chan receive]:
...
github.com/zalando/go-keyring.secretServiceProvider.Set(...)
github.com/zalando/go-keyring@.../secret_service/...
...
with a companion goroutine permanently blocked reading from the D-Bus unix socket during the SASL/EXTERNAL auth handshake:
github.com/godbus/dbus/v5.(*Conn).inWorker(...)
github.com/godbus/dbus/v5@v5.2.2/conn.go:390
net.(*UnixConn).ReadMsgUnix(...)
internal/poll.(*FD).ReadMsg(...)
So: the CLI opens its own new D-Bus connection (rather than reusing/sharing the session bus connection other tools use) to talk to the Secret Service, and on this machine that connection's auth handshake never resolves — even though the exact same bus is trivially reachable via dbus-send in another process at the same moment. Whatever the cause on the D-Bus/gnome-keyring side, the CLI has no timeout around this call, so it wedges the whole process (and, transitively, auth login, auth status, and doctor, since they all go through the same credential store).
Confirmed workaround
Setting BASECAMP_NO_KEYRING=1 (an env var already compiled into the binary, presumably intended for exactly this) skips the keyring path entirely and every command returns instantly:
$ BASECAMP_NO_KEYRING=1 basecamp auth status --json
{"ok":true,"data":{"authenticated":true,...}} # instant
This isn't documented anywhere in --help output (auth --help, config --help, top-level --help), so it took a strings pass over the binary to discover. Also worth noting: basecamp already has a message for the explicit-error keyring-unavailable case ("system keyring unavailable (%v), credentials stored in plaintext at %s"), it just doesn't reach that fallback when the keyring call hangs instead of erroring.
Suggested fixes
- Wrap the keyring Set/Get calls in a bounded context/timeout (a few seconds is plenty) and fall back to the existing plaintext-storage path on timeout, the same as the existing "keyring unavailable" error path.
- Document
BASECAMP_NO_KEYRINGin--helpoutput and/orbasecamp doctor, so people hitting this don't have to reverse-engineer it. - Possibly related to the gnome-keyring Secret Service/D-Bus issues already tracked in
basecamp/hey-cli#352andbasecamp/hey-cli#453(see also this comment diagnosing the OpenSession/GVariant assertion chain on GNOME Keyring 50.0 on Omarchy) — different symptom (daemon aborts vs. a client-side hang with no timeout), same subsystem (zalando/go-keyring+ gnome-keyring D-Bus Secret Service on Omarchy/Hyprland), so may share a root cause worth checking together.
Happy to provide the full goroutine dump or test further if useful.
Activity
robzolkos commentedon Sep 30, 2026
Confirmed the indefinite-wait bug with an automated regression exercising the real credential-store → go-keyring → godbus path against a Unix socket that stalls D-Bus authentication. The test-only commit fails with the desktop read still blocked after 12 seconds; headless and explicit-bypass controls pass. With the fix, that desktop read returns after approximately 10 seconds.
Two dependent PRs are open for review:
BASECAMP_NO_KEYRINGhelp.The initial probe retains warned file fallback. A timeout after an established keyring operation is an error, not a silent switch to plaintext or a missing-login diagnosis. Started writes/deletes can still complete after timeout, so that store refuses further operations. Interactive macOS/Windows behavior is unchanged.
Both repositories' full local checks and all applicable hosted checks are green. Fresh Fizzy tests and command race tests pass against the shared library; HEY owns a separate store. No Basecamp API/SDK change is needed. Red-test-before-fix commits are preserved in both PRs.
Neither PR is merged or has auto-merge enabled. The shared-library PR should land first after review. The specific GNOME Keyring crash relationship remains unverified; the stalled-handshake reproduction establishes the CLI's missing bounds.
The existing immediate opt-out is
BASECAMP_NO_KEYRING=1; it uses plaintext file storage rather than the system keyring.