n8n-io/n8n · error · Error

MCP api-key rotate endpoint returned a redacted key — cannot

Error message

MCP api-key rotate endpoint returned a redacted key — cannot stage it for `claude` MCP auth

What it means

After rotating the MCP key, the client checks whether the returned value contains '*' — a marker that the server redacted it (e.g. `******abcd`). JWTs are base64url and never contain '*', so its presence means the key is unusable for staging claude MCP auth. This guard exists because rotate is supposed to always return unredacted keys.

Source

Thrown at packages/@n8n/instance-ai/evaluations/clients/n8n-client.ts:663

	 * 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' });
	}

	/**
	 * Invite member users in one batched request. Requires an owner session.
	 * Returns one row per invitee, reporting rather than throwing on failure:
	 * n8n creates the user shells before it reports per-invite errors, so the

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Upgrade n8n to a version where rotate reliably returns the raw key.
  2. File a backend bug — the rotate contract was violated; do not attempt to unredact.
  3. As a workaround, create a brand-new MCP key via the UI/API and stage that manually.
Defensive patterns

Strategy: type-guard

Validate before calling

const key = data.apiKey;
if (typeof key !== 'string' || key.includes('*'))
  throw new Error('redacted key returned; cannot stage for claude MCP');

Type guard

const isUnredactedKey = (v: unknown): v is string =>
  typeof v === 'string' && v.length > 0 && !v.includes('*');

Try / catch

try { return await client.rotateMcpApiKey(); }
catch (e) {
  if (e instanceof Error && e.message.includes('redacted')) { /* file backend bug, do not use */ }
  else throw e;
}

Prevention

When it happens

Trigger: Server bug returning a redacted key despite the rotate contract; a backend that returns the GET-style redacted representation from the rotate endpoint.

Common situations: Pre-release n8n with a rotate bug; a fork that altered the rotate handler; race where rotate returns the cached redacted form.

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/fc0143fc6fb92d33. Report an issue: GitHub.