Error codes
Curiosity Workspace returns standard HTTP status codes with a JSON error body. Errors are retryable in some cases and final in others; this page summarizes the contract.
Error response shape
All errors share a single envelope:
{
"error": {
"code": "endpoint_not_found",
"message": "No custom endpoint named 'similar-tickets' is registered.",
"details": {
"endpoint": "similar-tickets"
}
},
"traceId": "0HMVGB1J8K8I3:00000007"
}
error.code— stable machine-readable identifier. Safe to switch on in client code.error.message— human-readable. May change between versions; do not match on it.error.details— optional, code-specific structured context (the field that failed validation, the missing scope, …).traceId— server-side correlation ID. Include it when filing a support request.
Status code groups
| Status | Meaning | Retryable? |
|---|---|---|
2xx |
Success | n/a |
400 Bad Request |
The request is malformed or violates an invariant | No — fix the request and resend |
401 Unauthorized |
Missing, expired, or invalid token | No — issue a new token |
403 Forbidden |
Authenticated, but lacking the required scope or permission | No — fix the scope/permission |
404 Not Found |
The named resource (endpoint, node, schema) does not exist | No |
409 Conflict |
A concurrent write would corrupt invariants (duplicate key, schema mismatch) | Sometimes — re-read and try again |
413 Payload Too Large |
Request body exceeds the configured limit | No — split the payload |
422 Unprocessable Entity |
Request shape is valid but the contents are invalid (validation failure inside a custom endpoint) | No |
429 Too Many Requests |
Rate limit exceeded | Yes — respect Retry-After header |
500 Internal Server Error |
Unhandled server-side error | Sometimes — back off and retry |
502 / 503 / 504 |
Upstream/queue saturation or restart | Yes — exponential backoff |
Common codes by area
Authentication and tokens
| Code | Status | Typical cause | Remediation |
|---|---|---|---|
missing_token |
401 | No Authorization header |
Add Authorization: Bearer <token> |
invalid_token_signature |
401 | MSK_JWT_KEY doesn't match the token |
Reissue the token from the current workspace |
token_expired |
401 | The token's exp claim is in the past |
Reissue |
token_revoked |
401 | Token was revoked from the admin UI | Reissue |
insufficient_scope |
403 | Token is missing the required scope | Create a token with the right scope |
Graph and schema
| Code | Status | Cause | Remediation |
|---|---|---|---|
node_not_found |
404 | The UID or key doesn't resolve | Verify the node was created and committed |
schema_not_registered |
409 | Tried to upsert a node before registering its schema | Call CreateNodeSchemaAsync<T>() first |
schema_conflict |
409 | Attempting to change a key field or property type incompatibly | Plan a schema migration |
duplicate_key |
409 | Two distinct upserts with the same key in the same batch | Deduplicate in the source |
commit_aborted |
409 | A concurrent write modified the same nodes | Re-read affected nodes and retry the commit |
Search
| Code | Status | Cause | Remediation |
|---|---|---|---|
search_index_unavailable |
503 | Index is mid-rebuild | Wait for the rebuild to finish, then retry |
embedding_provider_error |
502 | LLM/embedding provider returned an error | Check provider status; the workspace falls back to text retrieval automatically |
query_too_complex |
400 | Traversal would exceed safety bounds | Add Take(...) / Skip(...), or scope StartAt(...) more narrowly |
field_not_indexed |
400 | Trying to filter or facet on an unindexed field | Add the field under Settings → Search → Indexes |
Custom endpoints and AI tools
| Code | Status | Cause | Remediation |
|---|---|---|---|
endpoint_not_found |
404 | No endpoint with that name | Check spelling; ensure the endpoint is Active |
endpoint_compile_error |
500 | The endpoint's C# failed to compile on save | See the error in the endpoint editor; the previous version stays live until the new one compiles |
endpoint_runtime_error |
500 | Unhandled exception in your endpoint code | Inspect the endpoint logs and add error handling |
tool_invocation_failed |
500 | An AI tool threw during a chat turn | Inspect the tool call trace in the chat view |
external_provider_timeout |
504 | The LLM provider didn't respond in time | Increase the per-call timeout, or switch to a faster model |
Rate limiting
429 Too Many Requests responses include:
Retry-After: 12
Wait at least Retry-After seconds before retrying. The recommended client pattern is exponential backoff capped at 60 seconds, with jitter, and a maximum of 5 retries before surfacing the failure.
Retry policy template
async Task<T> WithRetry<T>(Func<Task<T>> call)
{
var delay = TimeSpan.FromSeconds(2);
for (var attempt = 1; ; attempt++)
{
try { return await call(); }
catch (CuriosityHttpException ex) when (attempt < 5 && ex.IsRetryable)
{
await Task.Delay(delay + TimeSpan.FromMilliseconds(Random.Shared.Next(0, 500)));
delay = TimeSpan.FromSeconds(Math.Min(delay.TotalSeconds * 2, 60));
}
}
}
CuriosityHttpException.IsRetryable is true for 429, 502, 503, 504, and any 5xx with no error.code set.
Logging on the server side
Server logs include the traceId of every error response. If you need to investigate a specific error from a user-supplied traceId, search the workspace logs for that string. See Monitoring for log routing.