{"record":{"id":"4c2e26dec171b924","repo":"gravitational/teleport","slug":"vnet-is-already-running","errorCode":null,"errorMessage":"VNet is already running","messagePattern":"VNet is already running","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"lib/vnet/daemon/common_darwin.go","lineNumber":42,"sourceCode":"import \"C\"\n\nimport (\n\t\"errors\"\n\t\"unsafe\"\n\n\t\"github.com/gravitational/trace\"\n\n\t\"github.com/gravitational/teleport/lib/utils/darwinbundle\"\n)\n\nvar (\n\t// vnetErrorDomain is a custom error domain used for Objective-C errors that pertain to VNet.\n\tvnetErrorDomain = C.GoString(C.VNEErrorDomain)\n\n\t// errorCodeAlreadyRunning is returned within [vnetErrorDomain] errors to indicate that the daemon\n\t// received a message to start after it was already running.\n\terrorCodeAlreadyRunning = int(C.VNEAlreadyRunningError)\n\terrAlreadyRunning       = errors.New(\"VNet is already running\")\n\n\t// errorCodeMissingCodeSigningIdentifiers is returned within [vnetErrorDomain] Obj-C errors and\n\t// transformed to [errMissingCodeSigningIdentifiers] in Go.\n\terrorCodeMissingCodeSigningIdentifiers = int(C.VNEMissingCodeSigningIdentifiersError)\n\terrMissingCodeSigningIdentifiers       = errors.New(\"either identifier or team identifier is missing in code signing information; is the binary signed?\")\n)\n\nvar (\n\t// nsCocoaErrorDomain is a generic error domain used in a lot of Apple's Cocoa frameworks.\n\tnsCocoaErrorDomain = \"NSCocoaErrorDomain\"\n\n\t// https://developer.apple.com/documentation/foundation/nsxpcconnectioninterrupted-swift.var\n\terrorCodeNSXPCConnectionInterrupted = int(C.NSXPCConnectionInterrupted)\n\terrXPCConnectionInterrupted         = errors.New(\"XPC connection interrupted\")\n\n\t// https://developer.apple.com/documentation/foundation/nsxpcconnectioncodesigningrequirementfailure-swift.var\n\terrorCodeNSXPCConnectionCodeSigningRequirementFailure = int(C.NSXPCConnectionCodeSigningRequirementFailure)\n\terrXPCConnectionCodeSigningRequirementFailure         = errors.New(\"code signing requirement failed\")","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/gravitational/teleport/blob/1283425b60ec5f60d509ba4c791183d452923ff7/lib/vnet/daemon/common_darwin.go#L24-L60","documentation":"errAlreadyRunning is a sentinel error in the VNet daemon package (lib/vnet/daemon/common_darwin.go:42). It is produced when the macOS VNet daemon receives a start request while an instance of the daemon is already running. The daemon maps the Objective-C error in the custom vnetErrorDomain with code VNEAlreadyRunningError to this Go sentinel, and the client translates it back when it sees that domain/code from C.StartVnet.","triggerScenarios":"Calling tsh VNet start (RegisterAndCall -> startByCalling) while a previous VNet daemon instance is still alive; or restarting VNet so quickly that the old daemon has not yet noticed the previous instance stopped and exited (startByCalling returns the error and RegisterAndCall waits 2*CheckUnprivilegedProcessInterval then retries once).","commonSituations":"Running `tsh vnet start` in a second terminal while VNet is already active; running VNet on two tsh clients under the same macOS user; stop-then-immediately-start races; stale daemon left running after a crash of the client process.","solutions":["Wait and retry: the client already waits 2*CheckUnprivilegedProcessInterval and calls the daemon again, so a transient race usually self-heals.","Check if a daemon instance is already running (launchctl print / ps for the VNet daemon job) and stop it before starting a new one.","If a stale daemon persists, reboot or explicitly bootout/kick the launchd job for the tsh daemon label."],"exampleFix":"// before: starting VNet unconditionally\nif err := client.RegisterAndCall(ctx, bundlePath, cfg); err != nil { return err }\n// after: tolerate the already-running case in the caller\nif err := client.RegisterAndCall(ctx, bundlePath, cfg); err != nil {\n    if errors.Is(err, vnetdaemon.ErrAlreadyRunning) {\n        return nil // VNet already active, nothing to do\n    }\n    return trace.Wrap(err)\n}","handlingStrategy":"retry","validationCode":"// check for an existing daemon job before starting\nout, err := exec.Command(\"launchctl\", \"print\", \"system/\"+daemonLabel).CombinedOutput()\nif err == nil && !bytes.Contains(out, []byte(\"could not find service\")) {\n    // daemon already loaded; skip start or stop it first\n}","typeGuard":"func isAlreadyRunning(err error) bool { return errors.Is(err, vnetdaemon.ErrAlreadyRunning) }","tryCatchPattern":"if err := client.RegisterAndCall(ctx, bundlePath, cfg); err != nil {\n    if errors.Is(err, vnetdaemon.ErrAlreadyRunning) {\n        // benign: daemon already active; optionally wait and retry once\n        return nil\n    }\n    return trace.Wrap(err)\n}","preventionTips":["Check daemon status (SMAppService/launchctl) before attempting to start.","Avoid rapid stop/start cycles; give the old daemon time to exit.","Serialize VNet start operations within the same user session."],"tags":["macos","vnet","daemon","xpc"],"backgroundTag":"daemon-already-running","analyzedSha":"1283425b60ec5f60d509ba4c791183d452923ff7","analyzedAt":"2026-09-02T04:06:41.601Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}