{"record":{"id":"24dc9c01d9411bee","repo":"netbirdio/netbird","slug":"clusteraddress-is-required","errorCode":null,"errorMessage":"clusterAddress is required","messagePattern":"clusterAddress is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"shared/management/client/rest/reverse_proxy_clusters.go","lineNumber":41,"sourceCode":"\t\treturn nil, err\n\t}\n\tif resp.Body != nil {\n\t\tdefer resp.Body.Close()\n\t}\n\tret, err := parseResponse[[]api.ProxyCluster](resp)\n\treturn ret, err\n}\n\n// Delete removes every self-hosted (BYOP) proxy registration for the given\n// cluster address owned by the calling account. Shared clusters operated by\n// NetBird cannot be deleted via this endpoint; the server returns 404 / 400\n// for cluster addresses the account does not own.\nfunc (a *ReverseProxyClustersAPI) Delete(ctx context.Context, clusterAddress string) error {\n\t// Guard against the empty input: url.PathEscape(\"\") returns \"\" which\n\t// would collapse the request URL onto the collection endpoint and\n\t// silently delete nothing (or 405 depending on routing).\n\tif clusterAddress == \"\" {\n\t\treturn errors.New(\"clusterAddress is required\")\n\t}\n\tresp, err := a.c.NewRequest(ctx, \"DELETE\", \"/api/reverse-proxies/clusters/\"+url.PathEscape(clusterAddress), nil, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif resp.Body != nil {\n\t\tdefer resp.Body.Close()\n\t}\n\treturn nil\n}\n","sourceCodeStart":23,"sourceCodeEnd":52,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/shared/management/client/rest/reverse_proxy_clusters.go#L23-L52","documentation":"Client-side guard in ReverseProxyClustersAPI.Delete against an empty cluster address. url.PathEscape(\"\") returns an empty string, which would collapse the DELETE path onto the collection endpoint /api/reverse-proxies/clusters and hit the wrong route. The error prevents that accidental request before any HTTP call is made.","triggerScenarios":"Calling Delete(ctx, \"\") because the variable holding the cluster address was never set, a struct/config field was left empty, or a loop iterated over an unset field.","commonSituations":"CI/Terraform scripts templating the address from an unset variable; YAML config with a missing byop.clusterAddress key; helper wrappers that drop the argument.","solutions":["Pass the exact non-empty cluster address returned by List/Create","Validate or default the config field before calling Delete","Skip and log when the address is empty instead of calling"],"exampleFix":"// before\nerr := restClient.ReverseProxyClusters.Delete(ctx, os.Getenv(\"BYOP_CLUSTER\"))\n\n// after\naddr := os.Getenv(\"BYOP_CLUSTER\")\nif addr == \"\" {\n\treturn fmt.Errorf(\"BYOP_CLUSTER is not set\")\n}\nerr := restClient.ReverseProxyClusters.Delete(ctx, addr)","handlingStrategy":"validation","validationCode":"if clusterAddress == \"\" {\n\treturn fmt.Errorf(\"clusterAddress is required before deleting a BYOP cluster\")\n}\nerr := restClient.ReverseProxyClusters.Delete(ctx, clusterAddress)","typeGuard":null,"tryCatchPattern":"if err := restClient.ReverseProxyClusters.Delete(ctx, addr); err != nil {\n\tif err.Error() == \"clusterAddress is required\" {\n\t\t// config bug: the address never made it into this code path\n\t}\n\treturn err\n}","preventionTips":["Validate required string arguments at the config boundary, not at the HTTP call","Source cluster addresses from List/Create output rather than hand-typed config","Fail fast on unset env vars in deployment scripts"],"tags":["rest","validation","reverse-proxy","byop"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}