{"record":{"id":"5062f8139f24e25b","repo":"tailscale/tailscale","slug":"unexpected-non-flag-arguments","errorCode":null,"errorMessage":"unexpected non-flag arguments","messagePattern":"unexpected non-flag arguments","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/tailscale/cli/exitnode.go","lineNumber":86,"sourceCode":"\t\t\t\t\t\tName:       \"disconnect\",\n\t\t\t\t\t\tShortUsage: \"tailscale exit-node disconnect\",\n\t\t\t\t\t\tShortHelp:  \"Disconnect from current exit node, if any\",\n\t\t\t\t\t\tExec:       exitNodeSetUse(false),\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t})()...),\n\t}\n}\n\nvar exitNodeArgs struct {\n\tfilter string\n\tprobe  bool\n}\n\nfunc exitNodeSetUse(wantOn bool) func(ctx context.Context, args []string) error {\n\treturn func(ctx context.Context, args []string) error {\n\t\tif len(args) > 0 {\n\t\t\treturn errors.New(\"unexpected non-flag arguments\")\n\t\t}\n\t\terr := localClient.SetUseExitNode(ctx, wantOn)\n\t\tif err != nil {\n\t\t\tif !wantOn {\n\t\t\t\tpref, err := localClient.GetPrefs(ctx)\n\t\t\t\tif err == nil && pref.ExitNodeID == \"\" {\n\t\t\t\t\t// Two processes concurrently turned it off.\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn err\n\t}\n}\n\n// runExitNodeList returns a formatted list of exit nodes for a tailnet.\n// If the exit node has location and priority data, only the highest\n// priority node for each city location is shown to the user.","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/tailscale/tailscale/blob/cfe32b8be6a33f8e24fbc369cbfbf7c729d9e042/cmd/tailscale/cli/exitnode.go#L68-L104","documentation":"'tailscale exit-node up' and 'down' are thin toggles over localClient.SetUseExitNode(wantOn); they accept only flags, no positional arguments. Any extra word is rejected before the daemon call. When turning the node off, the command also tolerates a concurrent 'someone already turned it off' race.","triggerScenarios":"Passing an exit node name positionally, e.g. 'tailscale exit-node up my-node'. Selecting a specific exit node is not this command's job.","commonSituations":"Assuming 'exit-node up <name>' selects that node, mirroring 'tailscale up --exit-node=<name>'.","solutions":["Toggle only: tailscale exit-node up / tailscale exit-node down","To choose a node: tailscale set --exit-node=<name-or-IP> (or tailscale up --exit-node=...)"],"exampleFix":"# before\n$ tailscale exit-node up my-node\nerror: unexpected non-flag arguments\n\n# after\n$ tailscale set --exit-node=my-node","handlingStrategy":"validation","validationCode":"// up/down accept no positionals; steer users to 'set':\nif len(args) > 0 {\n    return fmt.Errorf(\"to select an exit node run: tailscale set --exit-node=%s\", args[0])\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Reserve up/down for toggling; select nodes with 'tailscale set --exit-node='","Keep flags in the FlagSet, not as positional words"],"tags":["cli","usage","exit-node"],"backgroundTag":null,"analyzedSha":"cfe32b8be6a33f8e24fbc369cbfbf7c729d9e042","analyzedAt":"2026-08-15T19:58:31.583Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}