Why your OpenAPI document isn't converting to MCP tools, and how to fix it
Find the message the importer gave you, learn what it means, check it against your document and decide what to change. Every message below is real output from Riviera's importer for a small test document with one defect, except the address check at import, which we ran separately.
In short
- Read the first cause, fix it, import again. The importer reports one cause per operation, the first it hits. Fixing it can reveal the next, so a document usually takes a few rounds.
- Three kinds of cause look alike. Something wrong in your document, a step you still have to take in the product, and something Riviera’s importer does not support. Each problem below says which it is, because the right response differs.
- Do not edit the document to make the message go away. The document is a contract with your API. A change that makes the importer accept it but no longer matches what your API sends, such as removing
nullablefrom a field that can be null, shows up when an agent calls the tool, as an output contract mismatch. - A tool in the list is not a tool that works. Analysis reads your document and never calls your API. The last section covers what only a real call can show.
This guide is for fixing one document. For what we measured across thirteen public documents, read what converts cleanly and what doesn’t. For every supported construct and exclusion code, see importing an API.
Reading the import result
After Analyze API, each operation is in one of three states: ready to add, needs a decision first, or can’t become a tool. An operation that can’t becomes a reason in the form CODE: detail. The code is stable, so search for it. The detail names the field or the rule.
A problem in the document itself, such as an unsupported version or invalid syntax, refuses the whole document and nothing is partly imported. Most problems in a single operation do not stop the others.
Every example below is a small document with a single defect. To try one yourself, replace https://api.example.com/v1 with a real public HTTPS address first: Riviera refuses a server that does not resolve on the public internet, and a placeholder host does not (see the server address).
Find your symptom
| What you see | Code | Usually | Go to |
|---|---|---|---|
| The whole document is rejected before any operation is listed | VERSION_UNSUPPORTED INVALID_YAML YAML_UNSUPPORTED WEBHOOKS_ONLY DOCUMENT_LIMIT OPERATION_LIMIT | Your document, or a limit | The whole document is refused |
| Every operation fails with the same message about the server | SERVER_REQUIRED SERVER_VARIABLES ORIGIN_UNSAFE | Your document | The server address |
| Operations fail on authentication, or all wait for a choice | SECURITY_ALTERNATIVES SECURITY_COMBINED SECURITY_UNSUPPORTED HEADER_RESERVED SECURITY_SCOPES | A limit, or a step | Authentication |
| Create and update operations are missing, reads are fine | BODY_OPTIONAL MEDIA_UNSUPPORTED | Your document, or a limit | Request bodies |
| List operations are missing, or an operation says its response isn't described | RESPONSE_ROOT RESPONSE_REQUIRED RESPONSE_VARIANTS | Your document, or a limit | Responses |
| A message names a schema field such as nullable or oneOf | SCHEMA_UNSUPPORTED SCHEMA_TYPE | A limit | Schema constructs |
| A message about a reference | REF_SIBLINGS REF_CYCLE REF_UNSUPPORTED REF_MISSING | Your document, or a limit | References |
| Two operations with similar paths both fail, or a cookie is mentioned | PATH_AMBIGUOUS PARAMETER_UNSUPPORTED | Your document, or a limit | Paths and parameters |
| Operations are listed but none is selected or added yet | no code, a sentence | A step | Operations waiting for a decision |
| Tools exist, and a call fails | HTTP_BACKEND_ERROR HTTP_RESPONSE_INCOMPATIBLE | Your API, or the document no longer matches it | A generated tool is not a working tool |
The whole document is refused
Other refusals of the whole document: INVALID_JSON (a syntax error), DUPLICATE_KEY (the same key twice in one object), INVALID_STRUCTURE (for example info.title or info.version is missing), PATH_REF (a path item written as a $ref), DIALECT_UNSUPPORTED (jsonSchemaDialect is set), OPERATION_LIMIT and ORIGIN_LIMIT (more than ten different API addresses in one source). Operations using HEAD, OPTIONS or TRACE fail as METHOD_UNSUPPORTED and still count toward the 200. A webhooks section with an empty paths object is not an error: it gives zero operations.
The OpenAPI version isn't recognised
- Symptom
VERSION_UNSUPPORTED, with no operations listed.- Cause
- Riviera reads OpenAPI 3.0.0 to 3.0.4 and 3.1.0 to 3.1.2. A Swagger 2.0 file has a
swaggerfield instead ofopenapi. In YAML,openapi: 3.0is read as the number 3, not as a version with three parts. - How to check
- Look at the first line of the file. Swagger 2.0 starts with
swagger: "2.0". A two-part version such as3.0or3.1is refused as well. - What to do
- Write the full version, such as 3.0.3 or 3.1.0. Convert a Swagger 2.0 file to OpenAPI 3 with a converter you trust and review the result before importing it.
- Whose problem
- Your document (fixing it is enough), and a Riviera limit (the importer does not support it today).
VERSION_UNSUPPORTED: Use OpenAPI 3.0.0–3.0.4 or 3.1.0–3.1.2 as JSON or YAML.
In YAML, write the version unquoted only if it has three parts, such as 3.0.3.The document only describes webhooks
- Symptom
WEBHOOKS_ONLY.- Cause
- Webhooks are requests your API sends. An agent can only call operations under
paths. A document with both is fine: thewebhookssection is left out and counted in the result. - How to check
- Check that the document has a
pathsobject with operations in it. - What to do
- Import the document that describes the requests your customers make to you. If it exists only as a webhooks file, there is nothing here for an agent to call.
- Whose problem
- Your document (fixing it is enough).
YAML that is valid but refused, or a document that is too big
- Symptom
INVALID_YAMLnames a line.YAML_UNSUPPORTEDsays valid YAML uses something Riviera does not read.DOCUMENT_LIMITorOPERATION_LIMITfor size.- Cause
- Riviera reads one YAML 1.2 document and refuses features whose meaning differs between YAML versions: merge keys, custom tags, directives, numbers written with a leading zero or in hexadecimal or octal, and more than one document in a file. The document has to be 1 MiB or less and have at most 200 operations.
- How to check
- Search the file for
<<:and for---appearing twice. Check the size of the file and count the operations. A document that is only over the byte limit because of its formatting can be minified. The limits apply after YAML aliases are expanded. - What to do
- Write merged keys out in full, remove the extra documents, or split a large document into parts of at most 200 operations each. Keep only the operations an agent should call.
- Whose problem
- Your document (fixing it is enough), and a Riviera limit (the importer does not support it today).
The server address
Riviera calls your API from its own servers, so each operation needs exactly one absolute address to call, with no credentials, query string or fragment in it (SERVER_UNSAFE) and no traversal or encoded separators in its path (SERVER_PATH). This is the most common cause in the documents we ran, and the cheapest to fix.
No server, a relative server, or more than one
- Symptom
- Every operation fails with
SERVER_REQUIRED. - Cause
- OpenAPI lets
serversbe missing (then the document is relative to wherever it was fetched), be relative like/api/v3, or list several, such as production and sandbox. Riviera has to know which one to call, and it does not guess. - How to check
- Look for
serversat the top of the document, and for aserverslist on a path or an operation, which overrides it. The detail says which case it is. - What to do
- Declare one absolute HTTPS server URL, at the top of the document. If you keep a sandbox in the document, import a copy that lists only the server agents should call.
- Whose problem
- Your document (fixing it is enough), and a Riviera limit (the importer does not support it today).
servers: [] # or missing
Importer: SERVER_REQUIRED: Declare exactly one effective absolute server URL.
Server alternatives require review outside this importer.
servers: [{ url: "/api/v3" }]
Importer: SERVER_REQUIRED: Use an absolute execution server URL;
relative servers are unsupported.
servers: [{ url: "https://api.example.com/v1" },
{ url: "https://sandbox.example.com/v1" }]
Importer: SERVER_REQUIRED: Declare exactly one effective absolute server URL.
Server alternatives require review outside this importer.A server variable has no default
- Symptom
SERVER_VARIABLES: Server variable region needs a default value.- Cause
- A templated server, such as https://{region}.example.com, needs a default for every variable. Riviera calls the default. It will not choose between the values in an enum.
- How to check
- Open the
variablesobject under the server and check that each name in braces has adefault, that the default is one of theenumvalues if you list them, and that it is a plain host label or path segment. - What to do
- Add a default that is the region or environment agents should use. A default can fill a host label or a path segment. It cannot add a port or a second host.
- Whose problem
- Your document (fixing it is enough).
The server looks right, and the import still refuses it
- Symptom
ORIGIN_UNSAFE: Use an HTTPS public execution server. Private targets are blocked in production.- Cause
- The import checks the address after reading the document. It must be HTTPS and must resolve to a public address.
http://,localhost, a private IP, a host name that resolves only on your private network, and a host name that does not resolve are all refused. A public name behind a firewall passes this check and can still fail when a tool is called. A placeholder such asapi.example.comdid not resolve when we checked on 13 October 2026, so it falls in the last group. - How to check
- Check that the address is https. Look up the host name (for example with dig): every address it returns must be a public one, and the name must resolve at all. A host that resolves only on your private network is refused.
- What to do
- Use the public HTTPS address of your API. An API that is only reachable internally needs a public entry point before agents can use it through Riviera.
- Whose problem
- Your document (fixing it is enough), and a Riviera limit (the importer does not support it today).
This check runs when you choose Analyze API in the product. The importer's reading of the document alone does not make it, which is why the fixtures in this guide cannot show it. The checks we ran against the address code, with the production setting, refused http://api.github.com (HTTPS is required), https://localhost, https://10.0.0.5, https://api.example.com and a name under .invalid (the last two because they do not resolve), and accepted https://api.github.com.
Authentication
Riviera supplies the credential, so it has to know exactly which one. Where a document offers a choice, either you choose a method in the product, or the importer cannot.
The document lists more than one way to authenticate
- Symptom
SECURITY_ALTERNATIVESon every operation that lists them, orSECURITY_COMBINEDwhen one requirement names two schemes.- Cause
- In OpenAPI,
security: [{a: []}, {b: []}]means a or b, andsecurity: [{a: [], b: []}]means a and b together. Riviera supports one requirement per operation, so it cannot pick, and it cannot send two credentials. - How to check
- Check the top-level
securityand each operation’s ownsecurity. An operation’s overrides the top-level one, so look at both. - What to do
- Keep the one method agents should use, in a copy of the document you import. Your API still accepts the others, and the document no longer mentions them. If two schemes must be sent together, the importer cannot express that today.
- Whose problem
- A Riviera limit (the importer does not support it today), and your document (fixing it is enough).
security:
- token: []
- key: []
Importer: SECURITY_ALTERNATIVES: Multiple security alternatives are unsupported.
Supply one explicit requirement.
security:
- token: []
key: []
Importer: SECURITY_COMBINED: Combined security schemes are unsupported.
Supply one supported requirement.
security:
- token: [] # one requirement: acceptedBasic authentication, a key in the query string or a cookie
- Symptom
SECURITY_UNSUPPORTED: Use HTTP bearer, a header API key or supported OAuth.- Cause
- Riviera can supply an HTTP bearer token, an API key sent in a header, OAuth client credentials and per-user OAuth through your own authorization server. OpenID Connect is read as per-user OAuth. HTTP Basic, an API key in a query string or cookie, mutual TLS and any other scheme are not supported.
- How to check
- Look at the scheme the operation’s requirement points to, under
components.securitySchemes: itstypeand, for an API key, where it is sent (in). - What to do
- If your API also accepts the credential as a bearer token or a header, describe that. If it does not, there is nothing the importer can use, and the operation stays excluded.
- Whose problem
- A Riviera limit (the importer does not support it today).
The credential is declared as a header parameter
- Symptom
HEADER_RESERVED: The Authorization header carries credentials or routing, so an agent cannot be asked for it.- Cause
- A credential written as an ordinary parameter would be an argument the agent has to supply. Riviera keeps credentials out of the agent's hands, so it refuses header parameters that carry credentials or routing, such as Authorization, Proxy-Authorization and Host. Other credential-bearing headers, such as X-Api-Key, are not caught: they would be accepted as an argument the agent has to supply, so declare those as security schemes too.
- How to check
- Search the operation’s
parametersforin: headerentries namedAuthorizationor another reserved header such asHost,Content-LengthorConnection. Header parameters namedContent-Type,AcceptorCookiefail differently, asCONTRACT_INCOMPATIBLE. - What to do
- Remove the parameter and declare the credential as a security scheme, so Riviera supplies it.
- Whose problem
- Your document (fixing it is enough).
OAuth declared with a flow Riviera does not run
- Symptom
SECURITY_SCOPES: Declare one supported flow that grants all operation scopes.The message does not name the flow.- Cause
- Of the OAuth flows, Riviera supports client credentials and authorization code. A document that offers only the implicit flow (we ran that case) has no flow it can use, and the message reports it as a scope problem. Password is refused by the same rule.
- How to check
- Open the scheme under
components.securitySchemesand read itsflows. Check that the scopes the operation asks for are declared on a flow that is supported. - What to do
- Declare
authorizationCodefor per-user sign-in, orclientCredentialsfor a service credential, if your authorization server supports it. Supporting per-user sign-in also takes setup on your side: see authentication. - Whose problem
- A Riviera limit (the importer does not support it today), and your document (fixing it is enough).
Request bodies
Create and update operations fail, reads don't
- Symptom
BODY_OPTIONAL: Only required JSON request bodies on write methods are supported.- Cause
- OpenAPI treats a request body as optional unless it says
required: true. Many documents leave the flag out for bodies that are in practice required. Riviera only builds a tool from a body that is required, because an optional body would have to be sent or omitted on the agent’s guess. A body on a GET is refused for the same reason. - How to check
- Open the failing operation’s
requestBodyand look forrequired: truebesidecontent. - What to do
- Add
required: trueif the API rejects a call without a body. If the body really is optional, do not add it: agents would then always be asked for one. That operation stays excluded. - Whose problem
- Your document (fixing it is enough), and a Riviera limit (the importer does not support it today).
requestBody:
content:
application/json: { schema: { ... } }
Importer: BODY_OPTIONAL: Only required JSON request bodies on write methods are supported.
requestBody:
required: true
content:
application/json: { schema: { ... } } # a tool the user must still reviewForms, file uploads and a second body format
- Symptom
MEDIA_UNSUPPORTED: Declare only application/json for this body or response.- Cause
- Only
application/jsonbodies can become tools. Multipart and form bodies, XML, and binary uploads cannot. A body that listsapplication/jsonbeside another type is refused as well, because Riviera would have to choose. - How to check
- Count the keys under
content. There must be exactly one, and it must beapplication/json. - What to do
- Remove the other media types from the copy you import, if your API accepts or returns JSON for the same operation. The same rule applies to a response, and the key must be exactly application/json: application/json; charset=utf-8 is refused. If the operation is a file upload, it cannot be a tool today.
- Whose problem
- Your document (fixing it is enough), and a Riviera limit (the importer does not support it today).
Responses
An operation that returns a list
- Symptom
RESPONSE_ROOT: Use an object-root JSON response; scalar/array output contracts are unsupported.- Cause
- A tool's output is a JSON object. A response whose top level is an array or a single value cannot be one.
- How to check
- Open the success response’s schema. If it begins with
type: array, that is the cause. - What to do
- Editing the document cannot fix this. The schema has to describe what your API sends, and your API sends an array. The fix is in the API: return an object that wraps the list, such as { items: [...] }, and describe that. Until then the operation stays excluded.
- Whose problem
- A Riviera limit (the importer does not support it today).
The success response isn't described, or uses a wildcard
- Symptom
RESPONSE_REQUIRED, with eitherDeclare JSON response schemas, or an explicit no-content 204 response.orWildcard success codes are unsupported.- Cause
- The tool needs an output schema. A 200 with a description and no
content, a response keyed2XX, or an operation that declares onlydefaultand error responses, does not promise any particular result. Several success statuses with different schemas fail asRESPONSE_VARIANTS, because the tool has one output. That includes a 200 with a JSON body next to a 204. - How to check
- Open the operation’s
responses. Each 2xx entry needscontentwith anapplication/jsonschema and an object at its root, or the code must be 204 with no body. - What to do
- Describe the real response in the document. If every success status returns the same object, use one schema. Remove a status only if your API never returns it. If the shapes genuinely differ, such as a 200 with the resource and a 202 with a job id, the operation cannot be a tool today.
- Whose problem
- Your document (fixing it is enough), and a Riviera limit (the importer does not support it today).
Schema constructs
Riviera compiles each schema into a check that runs when an agent calls the tool, so it accepts only schemas it can check exactly. Nothing that changes what a value may be is dropped silently, except an unknown format, and example, default and deprecated are read and left out.
Nullable fields, unions and composition
- Symptom
SCHEMA_UNSUPPORTED: Schema field nullable is unsupported.The same foroneOfandallOf. In OpenAPI 3.1,type: ["string", "null"]givesSCHEMA_TYPE: Declare one explicit non-null JSON type.- Cause
nullable, type arrays,oneOf,anyOf,allOf,notanddiscriminatorare the common ones, and the list is open: the importer refuses any keyword that is not on its supported list, includingreadOnlyandwriteOnly(very common onidfields),x-nullable,xmlandexternalDocs. It reports the first one it meets. A schema with notypeat all, such as a field that only has adescription, fails asSCHEMA_TYPEtoo.- How to check
- Read the field name in the detail, then find it in the schemas the operation uses: its parameters, its request body and its success response. A schema in an error response or an example does not matter.
- What to do
- Only simplify a schema where the simpler version is true. Remove
nullableif the API never sends null. Give a field an explicittype. A field that really is a union of shapes cannot be described today, and the operation stays excluded. If you removenullablefrom a field that can be null, the tool will import and a real response will not match its output schema. ForreadOnlyandwriteOnly, removing the keyword from a response property is harmless, and removing it from a request property makes agents supply that field. - Whose problem
- A Riviera limit (the importer does not support it today).
note: { type: string, nullable: true }
Importer: SCHEMA_UNSUPPORTED: Schema field nullable is unsupported.
Use the documented explicit JSON schema subset.
note: { type: [string, "null"] } # OpenAPI 3.1
Importer: SCHEMA_TYPE: Declare one explicit non-null JSON type.
Nullable, unions and inferred types are unsupported.
payment: { oneOf: [ ... ] }
Importer: SCHEMA_UNSUPPORTED: Schema field oneOf is unsupported.
meta: { description: "Anything" } # no type
Importer: SCHEMA_TYPE: Declare one explicit non-null JSON type.Some things people expect to fail do not. A format Riviera does not know, such as ulid, is ignored and the operation still converts. So is a vendor extension in a schema, such as x-internal (but not x-nullable, which changes what a value may be), and so is a webhooks section.
References
Fields written beside a $ref
- Symptom
REF_SIBLINGS: Reference siblings are unsupported. Put fields in the referenced definition.- Cause
- In OpenAPI 3.0 anything written next to a
$refis ignored by the specification, and 3.1 allows some of it. Riviera does not read fields beside a$refin either version, and refuses the operation instead of dropping them without telling you. - How to check
- Search for a
$refline followed by another key in the same object. - What to do
- Move the description into the referenced definition, or remove it. Check that you are not dropping a constraint, such as a different maximum, that the sibling field was meant to add.
- Whose problem
- Your document (fixing it is enough).
Recursive, external and missing references
- Symptom
REF_CYCLEfor a schema that contains itself,REF_UNSUPPORTEDfor a reference to another file or URL, andREF_MISSINGfor a pointer that leads nowhere.- Cause
- Riviera follows local references, starting with #/, and only for what a tool uses: parameters, the request body and success responses. A tree that contains itself, such as a folder with child folders, has no finite schema. A reference to another file would need a fetch the importer does not make.
- How to check
- For
REF_MISSING, copy the pointer and look for exactly that path, including capitalisation. ForREF_UNSUPPORTED, search for$refvalues that do not start with#/. - What to do
- Bundle a multi-file document into one before importing. Correct a broken pointer. A recursive schema has to be replaced with one that stops at a fixed depth. That is true to your API only if it never nests deeper, so it is a design decision about the API, not a mechanical edit.
- Whose problem
- Your document (fixing it is enough), and a Riviera limit (the importer does not support it today).
Paths and parameters
Two paths with the same shape
- Symptom
PATH_AMBIGUOUSon both operations.- Cause
/invoices/{id}and/invoices/{number}are the same address with different names. A call could reach either, so Riviera refuses both rather than choose.- How to check
- List the paths and compare them with the placeholders replaced by braces.
- What to do
- Check whether they are one resource with inconsistent placeholder names, such as GET /invoices/{id} and PUT /invoices/{number}. If so, use the same placeholder name in the path and in the parameter's name, and merge the two path items, so no operation is lost. This only works when they use different methods: two GETs on one resource cannot be merged. If they really are two different lookups, give them different fixed segments in your API.
- Whose problem
- Your document (fixing it is enough).
A cookie, or a parameter with a complex value
- Symptom
PARAMETER_UNSUPPORTED: Use named path/query/header parameters; cookie parameters are unsupported.OtherPARAMETER_*codes cover object values and serialization styles.- Cause
- Tools take path, query and header parameters with simple values, and query arrays sent as repeated fields. Cookie parameters, object-valued parameters and other serialization styles are not supported.
- How to check
- Check each parameter’s
in, its schema’s type and itsstyle. - What to do
- If your API also accepts the value in a header or the query, describe that. Otherwise the operation stays excluded.
- Whose problem
- A Riviera limit (the importer does not support it today).
CONTRACT_INCOMPATIBLE is the importer’s catch-all for an operation it cannot map and has no more specific reason for: “Repair the path/mapping. Match each path placeholder, declare required scalar arguments, and remove credential/routing headers or unsupported serialization.” Check that every {placeholder} in the path has a path parameter of the same name, that each is marked required, and that no header parameter is on the reserved list above.
Operations waiting for a decision
An operation can be convertible and still not ready. These show a sentence, not a code, and they are steps in the product, not defects.
| What the importer says | Why | What to do |
|---|---|---|
| Review the write operation and its effects before enabling. | POST, PUT, PATCH and DELETE change data. Reads are selected for you, writes never are. | Select the write operations you want agents to use, on purpose. |
| This operation is deprecated in its source. Review before enabling. | The document marks it deprecated. | Decide whether an agent should still use it. |
| Choose static/service or per-user authentication, or the intended OAuth flow, before creating a Draft. | A bearer scheme can be a shared service token or a per-user credential. The document does not say which. | Choose the method when you add the operation. |
| Provide a matching project-scoped credential reference before creating a Draft. | An API key or a client-credentials scheme needs a credential for the exact server. | Save the credential for this API, then add the operation. |
| Delegated per-user OAuth is required. Configure the customer authorization server for this exact backend resource and scopes in Settings before enabling or publishing. | Per-user sign-in needs your authorization server set up for this API. | Follow authentication. |
In the research behind our other guide, 121 of the 124 operations that converted were waiting on one of these decisions, and the other 3 needed none. None was a tool in a draft yet. That is expected, not a failure.
Size and count limits
| Limit | Value | Code | What to do |
|---|---|---|---|
| Document size | 1 MiB | DOCUMENT_LIMIT | Minify if it is only formatting, or split the document. |
| Operations in one document | 200 | OPERATION_LIMIT | Split into documents of at most 200 operations, keeping the ones agents need. |
| Nesting and nodes | 40 levels deep, 50,000 nodes | DOCUMENT_LIMIT | Flatten or split. |
| Schema size | 5,000 values in one schema of a tool, 40,000 in distinct schemas for one import | SCHEMA_TOO_LARGE IMPORT_BUDGET | Remove fields an agent does not need, or split the document. |
| Reference expansion | 2 MiB of text or 250,000 values across the document | REF_LIMIT | Share fewer or smaller definitions. |
| API sources in a project | 10 | SOURCE_LIMIT | Reimport an existing source. |
We tested two of these. A document of exactly 200 operations is accepted and 201 is refused whole with OPERATION_LIMIT. A document padded with whitespace past 1 MiB is refused with DOCUMENT_LIMIT, and the same document without the padding is accepted. The other values are the importer’s documented limits.
A generated tool is not a working tool
Everything above is about whether a tool is generated. Analysis reads your document and never calls your API, so it cannot know whether the tool works. Agents read what you publish, not the draft, so a call is the first real test.
| Situation | What Riviera does | What to check |
|---|---|---|
| Your API answers with an error status | The agent is told the call failed with the HTTP status (HTTP_BACKEND_ERROR). The error body is not passed on. | Activity shows the tool, the outcome and the status. Read your API's own logs for the reason. |
| Your API answers 2xx, but not with JSON | The call is reported as HTTP_RESPONSE_INCOMPATIBLE. The request succeeded on your side. | Check the content type your API returns. Verify what the call changed before retrying a write. |
| Your API answers 2xx with JSON that doesn't match the output schema | The agent still receives the response and the call counts as a success, but Activity shows the diagnostic category “output contract”. | Compare a real response with the schema. This is where a removed nullable shows up. |
| Your API redirects | The redirect is not followed (HTTP_REDIRECT_REFUSED). | Describe the final address in the document, not the one that redirects. Check what happened to a write. |
| Your API is slow or large | A call that does not finish in 60 seconds is reported as a timeout, and the tool may still have run on your side: verify before retrying a write. A response over 5 MiB fails as an upstream error. | Check your API's response time and size for the arguments the agent sent. |
| The agent sends arguments that don't match the input schema | Riviera stops before calling your API and says so. | Activity shows that nothing ran on your side. |
To see a real call, publish a version, connect a client as described in connecting an AI client and open Activity. A model test is a different check: it asks which tool a model would choose and does not call your API. Nothing here says whether an agent will choose the tool or fill it in well. That needs its own test with the requests your customers make.
A repeatable way to work through it
- Fix anything that refuses the whole document first. Those errors hide every operation.
- Declare one absolute public HTTPS server. It clears the largest group of failures at once.
- Group the remaining operations by code. A few codes usually account for most of them.
- For each code, decide whether it is a defect in the document, a step to take, or a limit. Fix the first kind, take the second, and leave the third excluded rather than distort the document.
- Import the corrected document again. Riviera lists what was added, changed and removed, and your edits to tools are kept.
- Add the operations you want, resolve each decision, edit the names and descriptions agents will read, and publish.
- Make a real call and read it in Activity.
What this guide doesn’t cover
- The examples are small documents with one defect each. A real document has several, and only the first is shown for each operation, so expect more than one round.
- The messages are those of Riviera’s importer as of 13 October 2026. Another converter will refuse different things and say different words. Messages change as the importer does, and the codes are the stable part.
- We checked the importer’s analysis and address checks. We did not check any provider’s API, call a model or measure how often each cause occurs. For counts across real documents see the study, which is small and was chosen.
- The call-time behaviour in the table comes from reading the code that executes a call. We did not run a live call to produce each row.