{"record":{"id":"01648ea5ff31f5df","repo":"juanfont/headscale","slug":"deleting-api-key-w","errorCode":null,"errorMessage":"deleting api key: %w","messagePattern":"deleting api key: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/headscale/cli/api_key.go","lineNumber":186,"sourceCode":"\tRunE: clientRunE(func(ctx context.Context, client *clientv1.ClientWithResponses, cmd *cobra.Command, args []string) error {\n\t\tid, prefix, err := apiKeyIDOrPrefix(cmd)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// The DELETE route addresses the key by its prefix in the path. When the\n\t\t// user deletes by --id we resolve the id to its (masked) prefix first,\n\t\t// since the path segment is required and a query-only id cannot be routed.\n\t\tif prefix == \"\" {\n\t\t\tprefix, err = apiKeyPrefixForID(ctx, client, id)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\tresp, err := client.DeleteApiKeyWithResponse(ctx, prefix, &clientv1.DeleteApiKeyParams{})\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"deleting api key: %w\", err)\n\t\t}\n\n\t\tif resp.StatusCode() != http.StatusOK {\n\t\t\treturn apiError(resp.StatusCode(), resp.ApplicationproblemJSONDefault)\n\t\t}\n\n\t\treturn printOutput(cmd, resp.JSON200, \"Key deleted\")\n\t}),\n}\n\n// apiKeyPrefixForID resolves an API key id to its display prefix by listing the\n// keys. The DELETE endpoint addresses keys by prefix in the URL path, so a\n// delete by --id needs the prefix; the returned masked prefix is accepted by\n// the server's lookup.\nfunc apiKeyPrefixForID(\n\tctx context.Context,\n\tclient *clientv1.ClientWithResponses,\n\tid uint64,","sourceCodeStart":168,"sourceCodeEnd":204,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/cmd/headscale/cli/api_key.go#L168-L204","documentation":"Transport error from 'headscale apikeys delete': the DELETE request (addressed by prefix in the URL path, possibly resolved from --id via apiKeyPrefixForID) failed at the HTTP layer before a response arrived. Note the delete-by-id path performs an extra list call first, so a failure here means the actual DELETE call failed, not the prefix lookup.","triggerScenarios":"Deleting an API key while the server is unreachable or restarting; TLS/cert mismatch; context timeout on a slow link.","commonSituations":"Automation running deletes against a server mid-restart; CLI pointing at a stale address after a server migration; proxy interference with DELETE verbs.","solutions":["Verify connectivity ('headscale health')","Confirm address/TLS config, then retry the delete","If deleting by --id failed after the prefix lookup succeeded, re-run with --prefix directly to skip the extra list round trip"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// prefer prefix to skip the id->prefix resolution round trip\nif prefix != \"\" { /* single DELETE call, fewer failure points */ }","typeGuard":null,"tryCatchPattern":"if _, err := client.DeleteApiKeyWithResponse(ctx, prefix, params); err != nil {\n\tif isTransportError(err) { /* verify with list, then retry once */ }\n}","preventionTips":["Pass --prefix directly in automation to avoid the extra list dependency","Confirm deletes with a follow-up 'apikeys list' rather than blind retries"],"tags":["network","http-client","cli","go"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}