{"record":{"id":"235a8c181f68857f","repo":"netbirdio/netbird","slug":"tokenid-is-required","errorCode":null,"errorMessage":"tokenID is required","messagePattern":"tokenID is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"shared/management/client/rest/reverse_proxy_tokens.go","lineNumber":69,"sourceCode":"\t\tdefer resp.Body.Close()\n\t}\n\tret, err := parseResponse[api.ProxyTokenCreated](resp)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &ret, nil\n}\n\n// Delete revokes a previously-issued proxy token by ID. Revoked tokens\n// remain in List output (with revoked=true) so operators can audit which\n// credentials existed; the plain secret can no longer authenticate any\n// new proxy registration.\nfunc (a *ReverseProxyTokensAPI) Delete(ctx context.Context, tokenID 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 tokenID == \"\" {\n\t\treturn errors.New(\"tokenID is required\")\n\t}\n\tresp, err := a.c.NewRequest(ctx, \"DELETE\", \"/api/reverse-proxies/proxy-tokens/\"+url.PathEscape(tokenID), 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":51,"sourceCodeEnd":80,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/shared/management/client/rest/reverse_proxy_tokens.go#L51-L80","documentation":"Client-side guard in ReverseProxyTokensAPI.Delete against an empty proxy token ID. An empty ID would make url.PathEscape(\"\") collapse the DELETE URL onto the collection endpoint /api/reverse-proxies/proxy-tokens, deleting nothing or hitting the wrong route. The guard rejects the call before any request is issued.","triggerScenarios":"Calling Delete(ctx, \"\") with a token ID variable that was never populated, e.g. an unset env var, a zero-value struct field, or a lookup that returned no ID.","commonSituations":"Revocation scripts iterating tokens parsed from stale output; config keys for the token ID omitted; copy-paste helper calls missing the argument.","solutions":["Use the token ID exactly as returned by the Create/List endpoints","Validate the ID is non-empty before invoking Delete","Log and skip empty IDs when iterating a list of tokens to revoke"],"exampleFix":"// before\nerr := restClient.ReverseProxyTokens.Delete(ctx, tokenID)\n\n// after\nif tokenID == \"\" {\n\treturn fmt.Errorf(\"tokenID is required to revoke a proxy token\")\n}\nerr := restClient.ReverseProxyTokens.Delete(ctx, tokenID)","handlingStrategy":"validation","validationCode":"if tokenID == \"\" {\n\treturn fmt.Errorf(\"tokenID is required before revoking a proxy token\")\n}\nerr := restClient.ReverseProxyTokens.Delete(ctx, tokenID)","typeGuard":null,"tryCatchPattern":"if err := restClient.ReverseProxyTokens.Delete(ctx, tokenID); err != nil {\n\tif err.Error() == \"tokenID is required\" {\n\t\t// the ID lookup upstream produced nothing; fix the source of tokenID\n\t}\n\treturn err\n}","preventionTips":["Capture the ID from the Create response and persist it","Skip empty IDs when iterating lists of tokens to revoke","Validate identifiers at config load time"],"tags":["rest","validation","reverse-proxy","tokens"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}