Skip to content
NeuralRepo
Get Support

Error Handling

Every NeuralRepo API error returns a JSON object with an error field. The HTTP status gives the category; the body sometimes carries more.

Most errors are a single string:

{
"error": "Idea not found"
}

Some errors add fields alongside error. Where they do, error may be a machine-readable code rather than a sentence — POST /ideas over the Free-plan cap returns "error": "idea_limit_reached" with the human-readable text in message.

The path, body, or query parameters failed validation.

{
"error": {
"title": ["Too small: expected string to have >=1 characters"]
}
}

Common causes:

  • Missing required fields (title on idea creation, q on search)
  • A field over its limit (body > 50,000 characters, more than 20 tags, more than 50 bulk ids)
  • Invalid format (colour not #rrggbb, unparseable URL, unknown mode or link_type)
  • A non-numeric :id in the path — { "error": "Invalid idea ID" }
  • A relation that would create a cycle — despite the wording of the message, this is a 400

Authentication is missing or invalid. The body is always identical:

{
"error": "Unauthorized"
}

There is no separate message for expired, revoked, malformed, or absent credentials — all four produce this exact response. Common causes:

  • No Authorization, X-API-Key, or cookie credential
  • An X-API-Key value that does not begin with nrp_ (it is ignored entirely)
  • A session older than 30 days with no activity
  • A revoked key

Authenticated, but not allowed. Three distinct bodies:

Plan gatePOST /user/api-keys, BYOK save/test, relation writes, agent WebSocket:

{
"error": "Idea relations requires a Pro plan",
"pro_required": true,
"feature": "Idea relations",
"upgrade_url": "https://neuralrepo.com/upgrade"
}

Free-plan idea capPOST /ideas only:

{
"error": "idea_limit_reached",
"message": "You've captured 50 ideas on the free plan. Upgrade to Pro for unlimited ideas.",
"currentCount": 50,
"limit": 50,
"upgradeUrl": "https://neuralrepo.com/upgrade",
"featureRequestUrl": "https://support.neuralrepo.com/feature-requests"
}

Scope — MCP OAuth tokens only:

{
"error": "Insufficient scope: requires ideas:write"
}

Branch on pro_required and on error === "idea_limit_reached" rather than on the sentence.

The resource does not exist, or belongs to another account — the two are deliberately indistinguishable.

{
"error": "Idea not found"
}

Common causes:

  • An id that is not yours
  • Passing a display number (#42) where the API wants a database id — the single most common cause of a spurious 404
  • A duplicate detection that has already been dismissed or merged

Note that some deletes do not 404 on a bad id: DELETE /ideas/:id/links/:linkId and DELETE /map/relations/:id both return { "success": true } regardless.

Only two situations produce a 409:

{
"error": "Tag name already exists"
}
  • POST /tags with a name you already use.
  • POST /map/relations when a user-created relation already exists between that pair in that direction — { "error": "Relation already exists" }. Uniqueness ignores the relation type, so a second edge of a different type between the same two ideas also conflicts.

The daily quota is exhausted. See Rate Limits.

{
"error": "Rate limit exceeded"
}

No Retry-After header is sent — the response has no rate-limit headers of any kind. The window is a calendar day in UTC, so there is nothing to poll for until midnight.

An unhandled exception. In production the body is generic:

{
"error": "Internal server error"
}

The most reproducible cause is renaming a tag onto a name you already use — the unique constraint is not caught, so it surfaces here rather than as a 409.

An upstream service the request depends on failed:

EndpointCause
POST /ideas/:id/developYour BYOK provider rejected or failed the call; body includes provider
GET /user/support-linkThe support site could not mint a link

Returned only by GET /api/v1/health, when the database probe fails:

{
"status": "error",
"error": "Database unavailable"
}

Several operations report failure inside a 2xx response. Checking res.ok is not enough:

EndpointSuccess statusWhat to check
PATCH /ideas/bulk200errors count and the per-idea results array
POST /map/relations (bulk)201errors count and the per-link results array
POST /user/byok/:provider/test200ok — a rejected key is still a 200
GET /ideas/search200semantic_limit_reached, and search_type if you assumed semantic
GET /ideas/duplicates200On Free, the list is always empty regardless of what was detected
function formatError(error) {
if (typeof error === "string") return error;
if (error && typeof error === "object") {
return Object.entries(error)
.map(([field, msgs]) => `${field}: ${[].concat(msgs).join(", ")}`)
.join("; ");
}
return "Unknown error";
}
const res = await fetch("https://neuralrepo.com/api/v1/ideas", {
method: "POST",
headers: {
"X-API-Key": "nrp_YOUR_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({ title: "" }),
});
if (!res.ok) {
const body = await res.json();
switch (res.status) {
case 400:
console.error("Validation error:", formatError(body.error));
break;
case 401:
console.error("Auth error — check your API key");
break;
case 403:
console.error(
body.error === "idea_limit_reached"
? body.message
: body.pro_required
? `${body.feature} needs Pro: ${body.upgrade_url}`
: formatError(body.error)
);
break;
case 429:
// No Retry-After header exists — the quota resets at 00:00 UTC.
console.error("Daily rate limit exhausted");
break;
default:
console.error(`Error ${res.status}:`, formatError(body.error));
}
}
StatusNameRetryableerror typeDescription
400Bad RequestNoString or objectFix the request and resend; includes refused cycles
401UnauthorizedNoStringAlways "Unauthorized"
403ForbiddenNoString or codePlan gate, idea cap, or insufficient scope
404Not FoundNoStringWrong id — check you passed id, not number
409ConflictNoStringTag name taken, or that relation pair already exists
429Too Many RequestsNot todayStringQuota resets at 00:00 UTC; no Retry-After
500Internal Server ErrorSometimesStringRetry once; a tag rename collision will never succeed
502Bad GatewayYesStringUpstream AI provider or support service
503Service UnavailableYesStringHealth check only — the database is unreachable