{"record":{"id":"8a148ff4d1b83711","repo":"netbirdio/netbird","slug":"s-w","errorCode":null,"errorMessage":"%s: %w","messagePattern":"%s: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/cmd/daemon_error.go","lineNumber":23,"sourceCode":"\t\"fmt\"\n\t\"strings\"\n\n\t\"google.golang.org/genproto/googleapis/rpc/errdetails\"\n\tgstatus \"google.golang.org/grpc/status\"\n\n\t\"github.com/netbirdio/netbird/client/internal/ipcauth\"\n)\n\n// daemonCallError prepares a daemon error for display. A refusal the daemon\n// raised because the operation needs root/administrator is already guidance\n// written for the user, so it is surfaced on its own instead of buried under the\n// gRPC envelope and the name of the RPC that hit it. Anything else is wrapped\n// with context as usual.\nfunc daemonCallError(context string, err error) error {\n\tif guidance, ok := privilegeGuidance(err); ok {\n\t\treturn errors.New(guidance)\n\t}\n\treturn fmt.Errorf(\"%s: %w\", context, err)\n}\n\n// privilegeGuidance renders the daemon's privilege refusal as a summary and the\n// command that performs the operation with the privileges it needs. It reports\n// false for any other error.\nfunc privilegeGuidance(err error) (string, bool) {\n\tinfo, ok := privilegeErrorInfo(err)\n\tif !ok {\n\t\treturn \"\", false\n\t}\n\n\tsummary := info.GetMetadata()[ipcauth.ErrorMetaSummary]\n\tcommand := info.GetMetadata()[ipcauth.ErrorMetaCommand]\n\tif summary == \"\" {\n\t\t// Detail without a summary: fall back to the status message, which\n\t\t// carries the same text.\n\t\tsummary = strings.TrimSpace(gstatus.Convert(err).Message())\n\t}","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/cmd/daemon_error.go#L5-L41","documentation":"daemonCallError is the CLI's wrapper for daemon RPC failures. If the daemon refused the call because it needs root/administrator, the pre-written privilege guidance is surfaced alone instead (errors.New(guidance)). Otherwise — every non-privilege failure — the error is wrapped as '<context>: <original>', so this exact message shape means a daemon call failed for a reason other than missing privileges: daemon unreachable, RPC deadline, context canceled, or an internal daemon error, with the context string naming the operation (e.g. 'bundle debug').","triggerScenarios":"netbird debug bundle when the daemon is stopped or the unix socket/named pipe is unreachable (connection refused / timeout inside 'bundle debug: ...'); RPC deadline exceeded on a slow debug-bundle generation; daemon crashed mid-RPC; any future caller of daemonCallError whose RPC fails without the PRIVILEGE_REQUIRED ErrorInfo detail.","commonSituations":"Service not running on a fresh boot; user lacks permission to talk to the daemon socket (surfaces as a different refusal unless annotated as privilege guidance); very large log sets making DebugBundle exceed the client deadline; mismatched CLI/daemon versions where the RPC method behaves differently.","solutions":["Check the daemon state first: systemctl status netbird (or the platform service manager), and start it if stopped","Read the wrapped cause after the colon — 'connection refused' means socket/pipe unreachable, 'context deadline exceeded' means the daemon was slow, and the daemon log holds the detail","For bundle timeouts, retry with fewer log files (lower --log-file-count flag) or a healthier disk","Confirm CLI and daemon versions match (netbird version vs daemon-reported version) after upgrades"],"exampleFix":"# before: invoking while the service is down\nnetbird debug bundle\n# -> bundle debug: failed to connect ...\n\n# after: ensure the daemon is up, then bundle\nsudo systemctl start netbird && netbird debug bundle","handlingStrategy":"try-catch","validationCode":"// Pre-flight the daemon before the RPC:\nconn, err := grpc.Dial(daemonSock, grpc.WithBlock(), grpc.WithTimeout(2*time.Second))\nif err != nil {\n    return fmt.Errorf(\"daemon not reachable; start the service first\")\n}","typeGuard":"// Detect the privilege-refusal path (surfaced without context) vs the wrapped path:\nfunc isPrivilegeRefusal(err error) bool {\n    _, ok := privilegeErrorInfo(err)\n    return ok\n}","tryCatchPattern":"// Branch on which of the two shapes daemonCallError produced:\nif err := cmd(); err != nil {\n    if isPrivilegeRefusal(err) {\n        printGuidanceAndExit(err) // message already contains the sudo command\n    }\n    // wrapped shape '<context>: <cause>'\n    log.Errorf(\"%v\", err)\n}","preventionTips":["Check service state before invoking daemon-backed commands","Keep CLI and daemon versions in lockstep across upgrades","For long operations like debug bundle, pass conservative flags (--log-file-count) to stay under RPC deadlines"],"tags":["grpc","daemon","cli","error-handling","service"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}