calcom/cal.diy · error · UnauthorizedException
ApiKeysService - provided api key is not valid.
Error message
ApiKeysService - provided api key is not valid.
What it means
Thrown by ApiKeysService.refreshApiKey when the SHA-256 hash of the stripped API key does not match any record in the apiKey table via apiKeysRepository.getApiKeyFromHash. The refresh endpoint requires the caller to provide their existing valid API key, and if that key cannot be found (deleted, expired, wrong prefix, or incorrect), the refresh is rejected with UnauthorizedException (HTTP 401).
Source
Thrown at apps/api/v2/src/modules/api-keys/services/api-keys.service.ts:67
},
},
input: {
note: createApiKeyInput.note,
neverExpires: !!createApiKeyInput.apiKeyNeverExpires,
expiresAt: apiKeyExpiresAt,
teamId: createApiKeyInput.teamId,
},
});
return apiKey;
}
async refreshApiKey(authUserId: number, apiKey: string, refreshApiKeyInput: RefreshApiKeyInput) {
const strippedApiKey = stripApiKey(apiKey, this.config.get<string>("api.keyPrefix"));
const apiKeyHash = sha256Hash(strippedApiKey);
const apiKeyInDb = await this.apiKeysRepository.getApiKeyFromHash(apiKeyHash);
if (!apiKeyInDb) {
throw new UnauthorizedException("ApiKeysService - provided api key is not valid.");
}
const newApiKey = await this.createApiKey(authUserId, {
...refreshApiKeyInput,
note: apiKeyInDb.note || undefined,
teamId: apiKeyInDb.teamId || undefined,
});
await this.apiKeysRepository.deleteById(apiKeyInDb.id);
return newApiKey;
}
}
View on GitHub (pinned to 176037d0af)
Solutions
- Verify the API key is the current active key (not one that was already refreshed or deleted).
- Confirm the api.keyPrefix config value matches between the client and server so stripApiKey removes the correct prefix.
- If the key is lost or deleted, create a new one via POST /v2/api-keys instead of attempting to refresh.
- Check for trailing whitespace or newlines in the key value, which would produce a different SHA-256 hash.
Example fix
// before: attempting to refresh an already-refreshed key
await client.post('/v2/api-keys/refresh', {
body: { apiKey: oldKeyAlreadyDeleted }
});
// after: use the current key, or create a new one if lost
try {
await client.post('/v2/api-keys/refresh', {
body: { apiKey: currentActiveKey }
});
} catch (e) {
if (e.statusCode === 401) {
// key is invalid; create a fresh one
const { apiKey } = await client.post('/v2/api-keys', { body: { note: 'replacement' } });
return apiKey;
}
throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
// Before refreshing, verify the key format matches the expected prefix
const canRefreshKey = (key: string, prefix: string): boolean => {
return key.startsWith(prefix) && key.length > prefix.length + 5;
};
if (!canRefreshKey(currentKey, 'cal_')) {
throw new Error('Current API key is malformed; create a new key instead of refreshing.');
} Try / catch
// Handle refresh failure by falling back to key creation
const refreshOrCreate = async (client: ApiClient, currentKey: string): Promise<string> => {
try {
const { apiKey } = await client.post('/v2/api-keys/refresh', {
body: { apiKey: currentKey }
});
return apiKey;
} catch (err: any) {
if (err?.response?.status === 401) {
// Key is invalid or already refreshed; create a new one
const { apiKey } = await client.post('/v2/api-keys', {
body: { note: 'Replacement key' }
});
return apiKey;
}
throw err;
}
}; Prevention
- Store the most recent API key from the refresh response immediately and update all clients atomically.
- Never attempt to refresh a key that was already refreshed — the old key is deleted at refresh time.
- Keep an audit log of key refreshes to track which key is current.
- If the key is lost, use the create endpoint rather than retrying refresh.
When it happens
Trigger: Calling POST /v2/api-keys/refresh with a key that was already refreshed (the old key is deleted at line 76 during refresh). Using a key from a different environment (dev key against prod API). The API_KEY_PREFIX mismatch causing stripApiKey to remove the wrong prefix, producing a different hash than what's stored.
Common situations: Attempting to refresh an already-refreshed key (the old key is immediately deleted). Copying a key with truncation or extra characters. Environment mismatch: key created in staging but used against production. The key expired and was cleaned up by a background job.
Related errors
- CustomThrottlerGuard - Invalid API Key
- ApiKeysService - This endpoint can only be accessed using an
- ApiKeysService - No API key provided
- ApiKeysService -Cannot set both apiKeyDaysValid and apiKeyNe
- This event type requires authentication. Please provide vali
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/f7959fc7ff215830.
Report an issue: GitHub.