juanfont/headscale · warning
%w: api key %d not found
Error message
%w: api key %d not found
What it means
Returned by apiKeyPrefixForID when 'headscale apikeys delete --id N' cannot find any key whose Id matches N in the ListApiKeys response — the prefix resolution needed for the DELETE path failed. Wraps errMissingParameter, so errors.Is(err, errMissingParameter) is true.
Source
Thrown at cmd/headscale/cli/api_key.go:222
id uint64,
) (string, error) {
resp, err := client.ListApiKeysWithResponse(ctx)
if err != nil {
return "", fmt.Errorf("listing api keys: %w", err)
}
if resp.StatusCode() != http.StatusOK {
return "", apiError(resp.StatusCode(), resp.ApplicationproblemJSONDefault)
}
idStr := strconv.FormatUint(id, util.Base10)
for _, key := range resp.JSON200.ApiKeys {
if key.Id == idStr {
return key.Prefix, nil
}
}
return "", fmt.Errorf("%w: api key %d not found", errMissingParameter, id)
}
View on GitHub (pinned to 565fd254d0)
Solutions
- Run 'headscale apikeys list' and copy the current id or prefix
- If the key is already gone, the goal is achieved — treat as success in scripts
- Prefer --prefix in automation since it addresses the key directly without the list/resolve step
Example fix
# before headscale apikeys delete --id 42 # after headscale apikeys list headscale apikeys delete --prefix $(headscale apikeys list -o json | jq -r '.apiKeys[0].prefix')
Defensive patterns
Strategy: validation
Validate before calling
// verify the id exists before attempting delete-by-id
keys, err := listAPIKeys(ctx, client)
if err != nil { return err }
found := slices.ContainsFunc(keys, func(k ApiKey) bool { return k.Id == idStr })
if !found { return nil /* already gone: treat as success */ } Try / catch
if err := deleteKey(ctx, client, id); err != nil {
if strings.Contains(err.Error(), "not found") { return nil }
return err
} Prevention
- Treat 'not found' on delete as idempotent success in automation
- Refresh ids from 'apikeys list' at the start of each script run
- Prefer prefix addressing in scripts
When it happens
Trigger: Deleting by an --id that does not exist: already-deleted key, id from a different server/database, or a typo. The list call succeeds but no entry matches the formatted id string.
Common situations: Key was expired and pruned between listing and deleting; scripts reusing stale ids after a DB reset; confusing the key id with the node or user id.
Related errors
- either --id or --prefix must be provided: %w
- only one of --id or --prefix can be provided: %w
- parsing machine key: %w
- failed to parse ApiKey
- node not found
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/26b0033ccfd8572c.
Report an issue: GitHub.