floci-io/floci · error · AwsException
ApiKeyLimitExceededException
ApiKeyLimitExceededException
Error message
The API key exceeded a limit.
What it means
Thrown by createApiKey when the API already has 2 or more API keys (counted via apiKeyStore scan of keys prefixed apiId + '::'). This emulates AWS AppSync's per-API API-key quota (ApiKeyLimitExceededException).
Source
Thrown at src/main/java/io/github/hectorvent/floci/services/appsync/AppSyncService.java:576
if (request.containsKey("description")) existing.setDescription((String) request.get("description"));
if (request.containsKey("format")) existing.setFormat(parseEnum(TypeFormat.class, request.get("format")));
typeStore.put(apiKey(apiId, typeName), existing);
return existing;
}
public void deleteType(String apiId, String typeName) {
assertSchemaNotBusy(apiId);
getType(apiId, typeName);
typeStore.delete(apiKey(apiId, typeName));
}
// ──────────────────────────── API Keys ────────────────────────────
public ApiKey createApiKey(String apiId, Map<String, Object> request) {
getGraphqlApi(apiId);
long existingCount = apiKeyStore.scan(k -> k.startsWith(apiId + "::")).size();
if (existingCount >= 2) {
throw new AwsException("ApiKeyLimitExceededException",
"The API key exceeded a limit.", 400);
}
ApiKey key = new ApiKey();
key.setId(generateShortId());
key.setApiId(apiId);
key.setDescription((String) request.get("description"));
Object expiresValue = request.get("expires");
if (expiresValue instanceof Long l) {
key.setExpires(l);
} else if (expiresValue instanceof Number n) {
key.setExpires(n.longValue());
} else if (expiresValue instanceof String s) {
try {
key.setExpires(Long.parseLong(s));
} catch (NumberFormatException e) {
try {
key.setExpires(java.time.Instant.parse(s).getEpochSecond());
} catch (java.time.format.DateTimeParseException ex) {View on GitHub (pinned to 62ff490619)
Solutions
- Delete an existing API key first (deleteApiKey) so the count drops below the limit
- Reuse an existing key via updateApiKey (e.g. extend 'expires') instead of creating a new one
- If keys leaked from old tests, list and clean them up with listApiKeys + deleteApiKey
Example fix
// before
appSync.createApiKey(apiId, Map.of("description", "rotated")); // 3rd key -> limit
// after
var keys = appSync.listApiKeys(apiId, null, null).getItems();
appSync.deleteApiKey(apiId, keys.get(0).getId()); // drop oldest
appSync.createApiKey(apiId, Map.of("description", "rotated")); Defensive patterns
Strategy: validation
Validate before calling
var keys = appSync.listApiKeys(apiId, null, null).getItems();
if (keys.size() >= 2) {
appSync.deleteApiKey(apiId, keys.get(0).getId()); // drop oldest
}
appSync.createApiKey(apiId, request); Try / catch
try {
appSync.createApiKey(apiId, request);
} catch (AwsException e) {
if ("ApiKeyLimitExceededException".equals(e.getCode())) {
appSync.deleteApiKey(apiId, oldestKeyId);
appSync.createApiKey(apiId, request);
} else throw e;
} Prevention
- Delete-then-create when rotating API keys, never create-then-delete
- Track created key ids in tests and clean them up in teardown
When it happens
Trigger: Calling createApiKey on an apiId that already has two stored keys; e.g. creating a third key for rotation without deleting an old one.
Common situations: Key rotation scripts that create-then-delete instead of delete-then-create; tests that create fresh keys per iteration against the same API; long-lived emulator state accumulating keys across runs.
Related errors
- TooManyTagsException
- BadRequestException
- InvalidNextTokenException
- ResourceInUseException
- ValidationException
AI-assisted analysis of floci-io/floci@62ff490619 (2026-08-14).
Data as JSON: /api/errors/795985a56de4029b.
Report an issue: GitHub.