Skip to content

fix(desktop): don't crash when Prometheus discovery runs without a kubeconfig - #1787

Merged
nadaverell merged 1 commit into
mainfrom
fix/desktop-settings-crash-no-kubeconfig
Sep 16, 2026
Merged

nadaverell merged 1 commit into
mainfrom
fix/desktop-settings-crash-no-kubeconfig

Conversation

@nadaverell

@nadaverell nadaverell commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #1782

What

Radar Desktop crashed the moment you opened Settings (or the Cost page) on a machine with no usable kubeconfig — the exact state a fresh install lands in.

Why

Desktop deliberately keeps running when kubeconfig init fails so the UI can show the error. But RegisterCallbacks still passed k8s.GetClient() — a nil *kubernetes.Clientset — into prometheuspkg.Initialize / Reinitialize / traffic.ReinitializeWithConfig, all of which take kubernetes.Interface. A typed nil in an interface is non-nil, so the k8sClient == nil guard in discover() never fired. /api/opencost/summary → EnsureConnected → prom.Discover then dereferenced it inside a wg.Go worker, where chi's Recoverer can't catch the panic, and the process exited.

Fix

  • k8s.GetClientInterface() returns an untyped nil when there is no client; used at the three bootstrap sites that store the client in an interface.
  • Regression tests pin the untyped-nil and the pass-through case.

Verified

  • Reproduced on macOS with make desktop-binary + empty $HOME: click Settings → panic: nil pointer dereference in pkg/prom.collectWellKnownLocations, process gone.
  • With the fix, the same build answers /api/opencost/summary with {"available":false,"reason":"query_error",…} and POST /api/prometheus/connect with no Kubernetes client available for discovery; the process stays up.
  • go test ./internal/k8s ./internal/prometheus ./internal/app and pkg/prom green.

🤖 Generated with Claude Code


Note

Low Risk
Small, targeted change at client handoff sites with tests; no auth or data-path logic changes beyond preventing nil dereference when the cluster client is missing.

Overview
Fixes a Radar Desktop process exit when kubeconfig init fails but the UI still runs (e.g. fresh install with no kubeconfig). Opening Settings or hitting cost/Prometheus APIs could trigger discovery with a typed nil *kubernetes.Clientset stored in kubernetes.Interface, so downstream client == nil checks never ran and discovery panicked.

Adds k8s.GetClientInterface(), which returns an untyped nil when there is no clientset, and switches Prometheus/traffic bootstrap and reinit callbacks in RegisterCallbacks to use it instead of GetClient(). Regression tests lock in untyped-nil vs pass-through behavior.

Reviewed by Cursor Bugbot for commit 6edb345. Bugbot is set up for automated code reviews on this repo. Configure here.

…beconfig

Desktop stays up when kubeconfig init fails so the user can see the error,
but bootstrap still handed the Prometheus and traffic subsystems
k8s.GetClient() — a nil *Clientset that becomes a non-nil
kubernetes.Interface. The "no client" guard in discovery never fired, and
the first surface to kick discovery (Settings overview, Cost page, via
/api/opencost/summary) dereferenced it inside a discovery goroutine, where
no HTTP recoverer could catch the panic. The process died.

Add k8s.GetClientInterface(), which returns an untyped nil when no client
exists, and use it at every bootstrap site that stores the client in an
interface.

Fixes #1782
@nadaverell
nadaverell requested a review from hisco as a code owner September 16, 2026 10:42
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Prevent desktop crash when kubeconfig is unavailable

🐞 Bug fix 🧪 Tests 🕐 10-20 Minutes

Grey Divider

AI Description

• Prevents Prometheus discovery from receiving typed-nil Kubernetes clients when kubeconfig
 initialization fails.
• Uses nil-safe client handoffs for startup, Prometheus reinitialization, and traffic
 reinitialization.
• Adds regression coverage for absent and configured Kubernetes clients.
Diagram

graph TD
  A["Desktop bootstrap"] --> B["Client adapter"] --> C["Prometheus client"] --> D["Discovery request"] --> E{"Client missing?"}
  B --> G["Traffic manager"]
  E -->|Yes| F["Safe error"]
  E -->|No| H["Kubernetes API"]
Loading
High-Level Assessment

The boundary adapter is the most focused and idiomatic approach: it preserves existing concrete-client callers while ensuring interface-based consumers receive a true nil. Repeating nil checks at every bootstrap site or changing all downstream APIs to concrete clientsets would increase duplication and scope without improving behavior.

Files changed (3) +42 / -3

Bug fix (2) +16 / -3
bootstrap.goUse nil-safe Kubernetes interfaces for subsystem handoffs +3/-3

Use nil-safe Kubernetes interfaces for subsystem handoffs

• Routes initial Prometheus setup and Prometheus and traffic reinitialization through GetClientInterface. This allows downstream missing-client guards to run when kubeconfig initialization has failed.

internal/app/bootstrap.go

client.goAdd a nil-safe Kubernetes interface accessor +13/-0

Add a nil-safe Kubernetes interface accessor

• Adds GetClientInterface, which returns a nil interface when no clientset exists and otherwise passes through the configured clientset. This prevents a nil *Clientset from becoming a non-nil kubernetes.Interface.

internal/k8s/client.go

Tests (1) +26 / -0
client_interface_test.goCover missing and configured client interface behavior +26/-0

Cover missing and configured client interface behavior

• Adds regression tests confirming the accessor returns nil without a client and preserves the configured clientset when present. Test cleanup restores prior global client state.

internal/k8s/client_interface_test.go

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider

Great, no issues found!

Qodo reviewed your code and found no material issues that require review

Grey Divider

Tip of the day
💡 Did you know, you can group findings by type and pick your Finding display, from Minimal to Full

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

@nadaverell
nadaverell merged commit 0da1de7 into main Sep 16, 2026
9 checks passed
@nadaverell
nadaverell deleted the fix/desktop-settings-crash-no-kubeconfig branch September 16, 2026 10:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Crash when opening settings panel after a first fresh install

1 participant