fix(k8s): show actionable error when kagent CRDs are not installed#447
fix(k8s): show actionable error when kagent CRDs are not installed#447frivas-at-navteca wants to merge 2 commits intoagentregistry-dev:mainfrom
Conversation
When deploying agents to Kubernetes without kagent installed, the user received a raw 500 with a buried 'no matches for kind' message. This change detects the missing CRD condition and returns a clear, actionable error pointing to https://kagent.dev. Detection uses two strategies: - Primary: errors.As(*k8smeta.NoKindMatchError) with exact Group == "kagent.dev" and Kind scoped to the CRDs we actually deploy (Agent, McpServer) - Fallback: string-based check to handle controller-runtime wrapping the REST mapper error inside a StatusError, which breaks the typed chain Error is wrapped with %%w to preserve the chain for logging. Closes agentregistry-dev#318
|
You already have 3 pull requests open. Please consider working on getting the existing ones merged before opening new ones. Thanks! |
|
You already have 3 pull requests open. Please consider working on getting the existing ones merged before opening new ones. Thanks! |
There was a problem hiding this comment.
Pull request overview
This PR improves the Kubernetes deployment adapter’s error reporting when kagent CRDs are missing, so users get an actionable message instead of a cryptic “no matches for kind …” failure.
Changes:
- Add
isKagentCRDNotFoundError(err error) boolto detect missing kagent CRDs via typed (NoKindMatchError) and fallback string checks. - Update
Deploy()to return an actionable install hint when missing-CRD is detected (while preserving the original error via wrapping). - Add table-driven tests covering typed and fallback detection scenarios.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| internal/registry/platforms/kubernetes/deployment_adapter_kubernetes.go | Adds missing-CRD detection and returns a user-actionable error message on deploy. |
| internal/registry/platforms/kubernetes/deployment_adapter_kubernetes_platform_test.go | Adds unit tests for the missing-CRD detection helper. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // kagentKinds is the set of CRD Kinds we deploy via kagent. Checking Kind | ||
| // prevents false positives from other kagent.dev CRDs absent due to partial | ||
| // installs with a different remediation path. | ||
| kagentKinds := map[string]bool{ | ||
| "Agent": true, | ||
| "McpServer": true, | ||
| } |
There was a problem hiding this comment.
kagentKinds is allocated on every call. Since this helper can be called multiple times during a request (and the set is static), consider making it a package-level map[string]struct{} (or []string + switch) to avoid repeated allocations.
There was a problem hiding this comment.
@copilot apply changes based on this feedback
| // --- Fallback string path (StatusError wrapping breaks typed chain) --- | ||
| { | ||
| name: "fallback/StatusError-style wrapping preserves message text", | ||
| err: fmt.Errorf(`no matches for kind "Agent" in version "kagent.dev/v1alpha2"`), | ||
| want: true, | ||
| }, | ||
| { | ||
| name: "fallback/no matches for kind but unrelated group", | ||
| err: fmt.Errorf(`no matches for kind "Foo" in version "other.io/v1"`), | ||
| want: false, | ||
| }, | ||
| { | ||
| name: "fallback/contains kagent.dev but not the kind-match phrase", | ||
| err: fmt.Errorf("connection refused to kagent.dev endpoint"), | ||
| want: false, | ||
| }, |
There was a problem hiding this comment.
The fallback tests don’t include a case where the message contains kagent.dev + no matches for kind but the Kind is not one you deploy (e.g., UnknownKind in kagent.dev/v1alpha2). Adding that negative case would lock in the intended allowlist behavior and prevent regressions if the fallback matching is tightened.
There was a problem hiding this comment.
@copilot apply changes based on this feedback
| // Fallback: handle the case where controller-runtime wrapped the REST mapper | ||
| // error as a StatusError, breaking the typed chain. | ||
| msg := err.Error() | ||
| return strings.Contains(msg, "no matches for kind") && strings.Contains(msg, "kagent.dev") |
There was a problem hiding this comment.
The fallback string-matching path ignores the kagentKinds allowlist used in the typed NoKindMatchError path. That makes the behavior inconsistent with the function’s intent/comment (and could misclassify other kagent.dev kind-mismatch errors as “kagent not installed”). Consider extracting the Kind (and group) from the message and checking it against the same allowlist, or at least requiring the message to contain kind "Agent" or kind "McpServer" in addition to kagent.dev.
There was a problem hiding this comment.
@copilot apply changes based on this feedback
…rnetes.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
You already have 3 pull requests open. Please consider working on getting the existing ones merged before opening new ones. Thanks! |
Description
Motivation: When deploying agents to Kubernetes without kagent installed, users received a cryptic 500 Internal Server Error with a buried no matches for kind "Agent" in version "kagent.dev/v1alpha2" message that gave no actionable guidance.
What changed:
Fixes #318
Change Type
Changelog