Skip to main content

Three input mistakes, fixed

LLMs make three classes of input mistake against a snake_case schema:

The problem: JSON string instead of object

Where validation happens

The important consequence: anything that tries to coerce or reformat inside the execution step is dead code for the validation case, because the error fires before execution is entered.

The three layers

Layer 1 — alias acceptance

The contract answers to camelCase and PascalCase beside every snake_case field name, and to the original name. Without that, a camelCase key is unknown: it is dropped, the field stays None, serialization returns {}, and the API 400s with no local error to point at.

Layer 2 — JSON-string coercion

Before any field is type-checked, LLMBase parses string values that are obviously serialized JSON. The mistake is fixed silently; no error is ever raised.

Layer 3 — formatted errors

For genuine failures, validate_input raises ToolValidationError whose message is compact markdown naming each field path, the problem, and a truncated view of what was actually sent. Before:
After:

Catching it

Every error below derives from CharterError, so one except catches the whole surface:
catching_errors.py

In the LangChain adapter

to_langchain returns a StructuredTool with handle_validation_error wired to the same formatter, and returns ToolValidationError/APIError text as the tool result rather than raising. Agent frameworks expect a readable result they can feed back to the model, not an exception that ends the run.
  • ErrorsCharterError, DeclarationError, ToolValidationError, APIError, CredentialError, TransformError
  • Catching the surface — which exception to catch where
  • Tool.ainvoke — where validation runs, and what it raises