n8n-io/n8n · error · Error
MCP api-key rotate endpoint returned no apiKey
Error message
MCP api-key rotate endpoint returned no apiKey
What it means
rotateMcpApiKey POSTs to /rest/mcp/api-key/rotate, which should delete and recreate the key, returning an unredacted apiKey. If the response data has no apiKey field at all, the contract is broken and the caller cannot proceed — staging MCP auth would produce a broken config.
Source
Thrown at packages/@n8n/instance-ai/evaluations/clients/n8n-client.ts:658
}
}
/**
* Mint a fresh MCP API key for the authenticated user.
* POST /rest/mcp/api-key/rotate
*
* Uses rotate rather than GET /rest/mcp/api-key because the GET only returns
* the raw JWT when it creates the key; a pre-existing key comes back redacted
* (`******abcd`), which would silently break MCP auth if staged into a
* `claude` config. Rotate deletes + recreates, so the response is always
* unredacted — at the cost of invalidating any prior MCP key for this user.
*/
async rotateMcpApiKey(): Promise<string> {
const data = this.unwrapRestData<{ apiKey?: string }>(
await this.fetch('/rest/mcp/api-key/rotate', { method: 'POST' }),
);
if (!data.apiKey) {
throw new Error('MCP api-key rotate endpoint returned no apiKey');
}
// JWTs are base64url segments and never contain "*" — its presence means
// the server redacted the key, which would fail MCP auth downstream.
if (data.apiKey.includes('*')) {
throw new Error(
'MCP api-key rotate endpoint returned a redacted key — cannot stage it for `claude` MCP auth',
);
}
return data.apiKey;
}
/**
* Delete a credential by ID.
* DELETE /rest/credentials/:id
*/
async deleteCredential(id: string): Promise<void> {
await this.fetch(`/rest/credentials/${id}`, { method: 'DELETE' });
}View on GitHub (pinned to 5ac6606e81)
Solutions
- Upgrade the n8n instance to a version that implements POST /rest/mcp/api-key/rotate.
- Check the raw response (network tab / logs) to confirm the endpoint exists and returns the expected shape.
- If rotation is unavailable, fall back to manually creating an MCP key and staging it.
Defensive patterns
Strategy: try-catch
Validate before calling
// Probe endpoint capability before relying on rotate:
const probe = await fetch(`${baseUrl}/rest/mcp/api-key/rotate`, { method: 'OPTIONS' });
if (!probe.ok) throw new Error('rotate endpoint unavailable on this backend'); Type guard
const hasApiKey = (v: unknown): v is { apiKey: string } =>
typeof v === 'object' && v !== null && typeof (v as any).apiKey === 'string'; Try / catch
try { return await client.rotateMcpApiKey(); }
catch (e) {
if (e instanceof Error && e.message.includes('no apiKey')) { /* upgrade backend or stage manual key */ }
else throw e;
} Prevention
- Pin a backend version that implements rotate.
- Have a manual key-creation fallback documented.
- Log the raw rotate response when debugging.
When it happens
Trigger: Backend that does not implement the rotate endpoint; a future API change removing/renaming the field; a proxy returning an empty body.
Common situations: Pointing the eval client at an older n8n that lacks the rotate endpoint; version skew between client and server.
Related errors
- MCP api-key rotate endpoint returned a redacted key — cannot
- Failed to authenticate with n8n — no session cookie received
- Failed to enable MCP access (server reported mcpAccessEnable
- Invitation accepted but no session cookie received
- Restore was asked to seed ${String(agents.length)} agent(s)
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/a58d6a707414632f.
Report an issue: GitHub.