Skip to content
THIRI logo
build.thiri.ai
Music theory for agents

Docs · Errors

Errors

Every error is a JSON object with a stable code and a human message. Branch on the code; show the message.

Updated 2026-10-02

error shape
{
  "error": "invalid_chord",
  "message": "Could not parse: Xyz9"
}
CodeStatusWhenWhat to do
invalid_json400The body is not valid JSON.Send Content-Type: application/json and a JSON object.
invalid_input400A required field is missing or the wrong shape (no chord, progression not an array, previousVoicing without notes or midi, unrecognised key).The message names the field. See the field tables on the API page.
invalid_chord400 / 404The chord symbol could not be parsed. Reharmonize reports 400 with the bad symbols; analyze, resolve and voicing report 404.Use standard symbols: Cmaj7, Dm7b5, G7#9, C/E. Flats are b, sharps #.
unauthorized401Missing, revoked, expired or mistyped key.Check the Authorization header. Create or rotate a key at /keys.
forbidden403An admin-only route.Not something an API key can reach.
not_found404No such route.All theory endpoints are POST /v2/{analyze,resolve,voicing,reharmonize,conduct}.
method_not_allowed405GET on a POST endpoint.Use POST.
payload_too_large413Body over the size cap.Send one chord or one progression per call.
rate_limited429Per-minute limit (60 free, 120 Theory Pro, 300 Developer, 1,000 Licence) or the IP throttle.Back off and retry after a few seconds. Batch client-side.
quota_exceeded429Monthly quota spent on any tier.Wait for the first of the month or pick a larger plan on /keys.
server_error500Something broke on our side.Retry once; if it persists, email the request body and the time.

Retrying

Only 429 and 5xx are worth retrying. 4xx means the request itself needs to change.

backoff
async function thiri(path, body, tries = 3) {
  for (let i = 0; i < tries; i++) {
    const res = await fetch("https://chords.thiri.ai" + path, {
      method: "POST",
      headers: { Authorization: `Bearer ${process.env.THIRI_API_KEY}`, "Content-Type": "application/json" },
      body: JSON.stringify(body),
    });
    if (res.status !== 429 && res.status < 500) return res.json();
    await new Promise((r) => setTimeout(r, 500 * 2 ** i));
  }
  throw new Error("THIRI unavailable after retries");
}

Inside an MCP client

The MCP server surfaces the same codes as tool errors. Claude will usually read the message and correct the chord symbol on its own; if it keeps retrying the same bad input, tell it which field to change.

Next

Changelog →

What changed in the engine, the MCP server and this site.