derailed/k9s · error

failed to fetch source resource: %w

Error message

failed to fetch source resource: %w

What it means

customJump() first calls fetchResourceObject() to load the source object's full metadata for templating; this error wraps whatever failed inside (no accessor for the source GVR, or accessor.Get returning an API error such as NotFound or a 5xx). The %w keeps the underlying cause, so the real reason is appended to this message.

Source

Thrown at internal/view/custom_jump.go:28

	"log/slog"
	"strings"
	"text/template"

	"github.com/derailed/k9s/internal"
	"github.com/derailed/k9s/internal/client"
	"github.com/derailed/k9s/internal/config"
	"github.com/derailed/k9s/internal/dao"
	"github.com/derailed/k9s/internal/slogs"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/labels"
)

// customJump handles custom jumps between CRDs based on user configuration.
func customJump(app *App, sourceGVR *client.GVR, sourcePath string, rule *config.JumpRule) error {
	// Get the source resource to extract metadata for templating
	sourceObj, err := fetchResourceObject(app, sourceGVR, sourcePath)
	if err != nil {
		return fmt.Errorf("failed to fetch source resource: %w", err)
	}

	// Parse target GVR
	targetGVR := client.NewGVR(rule.TargetGVR)

	// Determine target namespace
	targetNS, err := determineTargetNamespace(sourcePath, rule.TargetNamespace, sourceObj)
	if err != nil {
		return err
	}

	// Use the registered viewer so built-in enter-chains (e.g. deploy -> pods -> logs) still work.
	var v ResourceViewer
	if mv, ok := customViewers[targetGVR]; ok {
		if mv.viewerFn != nil {
			v = mv.viewerFn(targetGVR)
		} else {
			v = NewBrowser(targetGVR)

View on GitHub (pinned to 2d3ccc6ba2)

Solutions

  1. Refresh the view and retry the jump so the row references a live object
  2. Read the wrapped cause: 'not found' means the object is gone; 403 means RBAC; connection errors mean cluster reachability
  3. Confirm the object with kubectl get <gvr> <path> using the same context
Defensive patterns

Strategy: try-catch

Validate before calling

// optional preflight: confirm the source row still exists before jumping
if _, err := fetchResourceObject(app, sourceGVR, sourcePath); err != nil {
    app.Flash().Errf("source object %s is gone or unreadable: %v", sourcePath, err)
    return
}

Try / catch

err := customJump(app, gvr, path, rule)
if err != nil {
    if strings.Contains(err.Error(), "failed to fetch source resource") {
        // surface the wrapped cause: NotFound -> stale row, 403 -> RBAC, else connectivity
        app.Flash().Errf("jump aborted: %v", errors.Unwrap(err))
    }
}

Prevention

When it happens

Trigger: Invoking a custom jump rule when the source row is stale (the object was deleted between listing and the jump), the GET against the API server fails, or the source GVR has no registered DAO.

Common situations: Resource deleted right before the jump (long-lived list view); RBAC denying get on the source resource; flaky API server / VPN drop; jump rule configured with a source that is not discoverable.

Related errors


AI-assisted analysis of derailed/k9s@2d3ccc6ba2 (2026-08-15). Data as JSON: /api/errors/4214d7a7d5527839. Report an issue: GitHub.