Errors
Error response formats, HTTP status codes, and OpenFeature error codes.
The Toggld API uses standard HTTP status codes. The response body format depends on the API surface:
| Surface | Error format |
|---|---|
| Management API, sync, and authentication | A JSON object with a _tag field that names the error |
| OFREP evaluation | An OFREP error object with key and errorCode |
| Rate limiting | A JSON object with a code field |
Management API errors
Management errors are JSON objects. _tag names the error, and message explains it. Some errors add fields.
{
"_tag": "FlagValidationError",
"flagKey": "new-checkout",
"issues": ["defaultVariant 'ghost' is not a declared variant"],
"message": "Flag 'new-checkout' is invalid: defaultVariant 'ghost' is not a declared variant"
}Use _tag in code. The message text can change.
| Status | _tag | Extra fields | Cause |
|---|---|---|---|
| 400 | None | None | The request body, path, or query does not match the expected shape, or it is not valid JSON. The response has no body. |
| 400 | EnvironmentRequired | The request reads or changes flag values, but it names no environment. API keys and public tokens always belong to an environment, so in practice only dashboard sessions get this error. | |
| 401 | Unauthorized | The credential is missing, unknown, or revoked. See Authentication. | |
| 402 | FlagLimitExceeded | limit, current | The workspace reached its flag limit. During the beta, the limit is ten flags. |
| 403 | Forbidden | The credential is valid but cannot use this endpoint. | |
| 404 | FlagNotFound | flagKey | No flag with this key exists in the credential's environment. |
| 404 | SegmentNotFound | segmentKey | No segment with this key exists. |
| 404 | EnvironmentNotFound | environment | No environment with this ID or key exists. |
| 404 | ApiKeyNotFound | keyId | No API key with this ID exists. |
| 409 | FlagAlreadyExists | flagKey | A flag with this key already exists. |
| 409 | FlagConflict | flagKey | The flag changed after you read it. Read it again, then retry. |
| 409 | SegmentAlreadyExists | segmentKey | A segment with this key already exists. |
| 409 | SegmentConflict | segmentKey | The segment changed after you read it. Read it again, then retry. |
| 409 | SegmentReferenced | segmentKey, flagKeys | The segment cannot be deleted, because the listed flags reference it. |
| 409 | EnvironmentAlreadyExists | environment | An environment with this key already exists. |
| 409 | EnvironmentConflict | The environment change conflicts with the workspace. You cannot delete the last environment, and a new order must list every environment exactly once. | |
| 422 | FlagValidationError | flagKey, issues | The flag is not valid. Each entry of issues describes one problem. |
| 422 | SegmentValidationError | segmentKey, issues | The segment or member change is not valid, for example more than 5,000 members. |
| 429 | None | code, message | You reached a rate limit. See below. |
| 503 | None | code, message | A required service is unavailable. Retry later. |
Common validation issues
FlagValidationError lists problems such as these:
- A variant value does not match the flag type.
- The default or off variant is not one of the variants.
- A rule serves a variant that does not exist.
- A rollout has no splits, has no positive weight, or has a negative weight.
- A condition operand has the wrong type for its operator. See Operators.
- A rule references a segment that does not exist.
- A request tries to change the flag type.
OFREP evaluation errors
The OFREP endpoints follow the OFREP specification.
Single evaluation
POST /ofrep/v1/evaluate/flags/{key} returns:
| Status | Body | Cause |
|---|---|---|
| 200 | { "key", "value", "reason", "variant", "metadata" } | Success. variant and metadata appear when present. |
| 400 | { "key", "errorCode", "errorDetails" } | Evaluation failed. errorDetails explains why. |
| 400 | { "key", "errorCode": "PARSE_ERROR", "errorDetails" } | The request body is not valid JSON. |
| 400 | { "key", "errorCode": "INVALID_CONTEXT", "errorDetails" } | The request body is JSON but does not match { "context": { … } }. |
| 401 | { "_tag": "Unauthorized", "message" } | The credential is missing or wrong. |
| 404 | { "key", "errorCode": "FLAG_NOT_FOUND" } | No flag with this key exists in the environment. |
{
"key": "new-checkout",
"errorCode": "TARGETING_KEY_MISSING",
"errorDetails": "Flag 'new-checkout' rule 'rollout' requires a bucketing key but none was provided"
}Bulk evaluation
POST /ofrep/v1/evaluate/flags returns HTTP 200 with { "flags": [...] }. Each entry is either a success or a per-flag error with key, errorCode, and optional errorDetails. Check each entry for errorCode.
If the request body is not valid, the API returns HTTP 400 with { "errorCode", "errorDetails" } and no key: PARSE_ERROR when the body is not valid JSON, and INVALID_CONTEXT when it does not match { "context": { … } }.
Error codes
Toggld uses the OpenFeature error codes.
| Error code | Where it comes from | Meaning |
|---|---|---|
FLAG_NOT_FOUND | API and SDK | No flag with this key exists in the environment. |
TARGETING_KEY_MISSING | API and SDK | A rollout matched, but the context has no usable bucketing key. See Missing bucketing key. |
GENERAL | API and SDK | Another failure. From the API, for example, a rule served a variant that does not exist. From the SDK, a network error, a timeout, a server error, or a rejected request such as HTTP 401 or 429. |
TYPE_MISMATCH | SDK | The flag value does not match the typed getter. |
PROVIDER_NOT_READY | SDK | A local-mode client has no snapshot yet. |
PARSE_ERROR | API and SDK | From the API, the request body is not valid JSON. From the SDK, it could not decode a response. |
INVALID_CONTEXT | API | The request body is JSON but not a valid evaluation request. |
PROVIDER_FATAL | Neither | Defined by OpenFeature and part of the response schema. The Toggld API and SDK do not return it today. |
When evaluation fails, the Node.js SDK returns your fallback value and reports reason: "ERROR" with the error code. See Error handling.
Rate limit errors
When you exceed a limit, the API returns HTTP 429 with a Retry-After header, in seconds:
HTTP/1.1 429 Too Many Requests
Retry-After: 12
Cache-Control: no-store{ "code": "RATE_LIMITED", "message": "Too many requests. Try again later." }If the rate limiter itself is unavailable, the API refuses the request with HTTP 503, Retry-After: 5, and the code RATE_LIMIT_UNAVAILABLE. See Rate limits.
Retry guidance
| Status | Retry? |
|---|---|
| 400, 401, 402, 403, 404, 422 | No. Fix the request or the credential first. |
| 409 | Read the resource again, then send a new request. |
| 429 | Yes, after the Retry-After interval. |
| 503 | Yes, after Retry-After when present, with backoff. |
| Network errors and timeouts | Yes, with backoff. For writes, read the resource first to check whether the earlier request succeeded. |