{"record":{"id":"26e091696c9c93ac","repo":"GoogleContainerTools/skaffold","slug":"forward-called-before-kubectl-forwarder-was-star","errorCode":null,"errorMessage":"Forward() called before kubectl forwarder was started","messagePattern":"Forward\\(\\) called before kubectl forwarder was started","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/skaffold/kubernetes/portforward/kubectl_forwarder.go","lineNumber":102,"sourceCode":"\tl := log.Entry(parentCtx)\n\tresourceName := \"\"\n\tif pfe != nil {\n\t\tresourceName = pfe.resource.Name\n\t}\n\tl.Tracef(\"KubectlForwarder.Forward(%s): waiting on errChan\", resourceName)\n\tselect {\n\tcase <-parentCtx.Done():\n\t\tl.Tracef(\"KubectlForwarder.Forward(%s): parentCtx canceled, returning nil error\", resourceName)\n\t\treturn nil\n\tcase err := <-errChan:\n\t\tl.Tracef(\"KubectlForwarder.Forward(%s): got error on errChan, returning: %+v\", resourceName, err)\n\t\treturn err\n\t}\n}\n\nfunc (k *KubectlForwarder) forward(ctx context.Context, pfe *portForwardEntry, errChan chan error) {\n\tif atomic.LoadInt32(&k.started) == 0 {\n\t\terrChan <- fmt.Errorf(\"Forward() called before kubectl forwarder was started\")\n\t\treturn\n\t}\n\tvar notifiedUser bool\n\tdefer deferFunc()\n\n\tfor {\n\t\tpfe.terminationLock.Lock()\n\t\tif pfe.terminated {\n\t\t\tlog.Entry(ctx).Debugf(\"port forwarding %v was cancelled...\", pfe)\n\t\t\tpfe.terminationLock.Unlock()\n\t\t\terrChan <- nil\n\t\t\treturn\n\t\t}\n\t\tpfe.terminationLock.Unlock()\n\n\t\tif !isPortFree(util.Loopback, pfe.localPort) {\n\t\t\t// Assuming that Skaffold brokered ports don't overlap, this has to be an external process that started\n\t\t\t// since the dev loop kicked off. We are notifying the user in the hope that they can fix it","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/GoogleContainerTools/skaffold/blob/a1189de023efc32d4b8e11f395acc678aa555011/pkg/skaffold/kubernetes/portforward/kubectl_forwarder.go#L84-L120","documentation":"KubectlForwarder.forward checks an atomic `started` flag before starting a port-forward loop; if Forward() was called before the forwarder's Start() routine was launched, the kubectl process plumbing (base command, kill channel) is not initialized, so it pushes this error onto errChan instead of panicking. It is a lifecycle/ordering guard for the forwarder's internal state machine.","triggerScenarios":"Calling Forward() on a KubectlForwarder instance without having called its Start() method first (or before the started goroutine has set the started flag), then supplying a portForwardEntry.","commonSituations":"Embedding KubectlForwarder in custom tooling and forgetting to call Start(); calling Forward() immediately after construction in tests; race where port-forward entries are queued before the forwarder's Run loop starts.","solutions":["Call the forwarder's Start()/Run method before invoking Forward()","Ensure port-forward entries are only submitted after the forwarder signals readiness","In tests, construct the forwarder the same way production code does, including the Start step"],"exampleFix":"// before\nfwd := NewKubectlForwarder(out, entrypoints)\nfwd.Forward(ctx, entry) // panics/errors: not started\n// after\nfwd := NewKubectlForwarder(out, entrypoints)\ngo fwd.Run(ctx, kubectl)\n// wait for readiness, then:\nfwd.Forward(ctx, entry)","handlingStrategy":"validation","validationCode":"// only submit port-forward entries after the forwarder is running\nselect {\ncase <-forwarderReady: // channel closed once Run/Start has begun\n\tforwarder.Forward(ctx, entry)\ncase <-time.After(5 * time.Second):\n\tlog.Fatal(\"kubectl forwarder did not start in time\")\n}","typeGuard":null,"tryCatchPattern":"errChan := make(chan error, 1)\ngo func() { forwarder.Forward(ctx, entry) }()\nselect {\ncase err := <-errChan:\n\tif strings.Contains(err.Error(), \"called before kubectl forwarder was started\") {\n\t\t// re-initialize: start forwarder, then re-Forward\n\t}\ncase <-ctx.Done():\n}","preventionTips":["Always call Start()/Run on the forwarder before queuing Forward calls","Initialize forwarders early in application startup, before port-forward entries are generated","In tests, mirror the production initialization sequence"],"tags":["port-forwarding","kubernetes","lifecycle"],"backgroundTag":"forwarder-not-started","analyzedSha":"a1189de023efc32d4b8e11f395acc678aa555011","analyzedAt":"2026-09-05T12:09:27.064Z","contentChangedAt":"2026-09-05T12:09:27.064Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}