Skip to content

fix: put the schema in the prompt when Anthropic refuses it as too complex - #16

Closed
berkayturk wants to merge 1 commit into
AienTech:mainfrom
berkayturk:fix/anthropic-schema-fallback
Closed

berkayturk wants to merge 1 commit into
AienTech:mainfrom
berkayturk:fix/anthropic-schema-fallback

Conversation

@berkayturk

Copy link
Copy Markdown

Problem

With the Anthropic provider (claude-sonnet-5-5), Profile → Import from a resume always fails with "The AI provider ran into a problem. Try again in a moment."

The API refuses the extracted_profile schema with a 400:

Schemas contains too many parameters with union types (32 parameters with type arrays or anyOf). This causes exponential compilation cost. Reduce the number of nullable or union-typed parameters (limit: 16 parameters with unions).

Making the schema's text fields non-nullable (32 → 13 unions) isn't enough. The same request is then refused with:

The compiled grammar is too large, which would cause performance issues. Simplify your tool schemas or reduce the number of strict tools.

I reproduced both errors with a direct /v1/messages call that used the task's schema and a three-line resume.

Fix

When a structured request fails with one of these "schema too complex" errors, AnthropicProvider sends it again with:

  • the schema in the system prompt, through withJsonSchema, which the OpenAI-compatible and Ollama providers already use;
  • no output_config.format.

The provider remembers this per server, model and schema name, so later calls skip the refused request. BaseProvider.complete still validates the answer against the schema and retries once if it doesn't match. Other errors are unchanged.

Tests

  • New test in src/lib/ai/providers.test.ts: a 400 "grammar is too large" leads to one retry with the schema in the prompt, and later calls go straight to the prompt.
  • Before the fix, this test failed with the same error users see. With the fix, providers.test.ts, structured-output.test.ts and extract-profile.test.ts pass (68 tests).
  • tsc --noEmit and eslint pass on the changed files.
  • Checked in a running instance: the resume import now works with claude-sonnet-5-5.

🤖 Generated with Claude Code

…mplex

Anthropic's structured outputs cap how complex a schema may be: at most 16
union-typed (nullable) parameters, and a size limit on the compiled grammar.
The profile import's schema has 32 nullable parameters, so every import from
a resume failed with "The AI provider ran into a problem". Making its text
fields non-nullable isn't enough: the API then refuses the schema because
"the compiled grammar is too large".

When the API answers a structured request with one of those errors, the
Anthropic provider now sends the request again with the schema in the
system prompt and no output_config format, as the other providers already
do, and remembers that per server, model and schema name. The answer is
still validated against the schema, with one retry, as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@philorien

philorien commented Oct 27, 2026 •

Copy link
Copy Markdown

Hit the same thing today and landed on the same fix independently. Confirming it isn't specific to sonnet-5-5: with the full extracted_profile schema I got the "compiled grammar is too large" 400 on claude-sonnet-5, claude-opus-5-5, claude-haiku-5-5 and claude-haiku-4-5, while small schemas went through fine. Falling back to the schema in the prompt fixed resume import on my instance.

AienTech added a commit that referenced this pull request Oct 29, 2026
FreeHire as a fourth job source, free and without a key, a line about the company or role under each job on your resumes, and fixes for importing a resume with Anthropic, for DeepSeek's answers getting cut off, for JSearch searches that took too long, and for servers without a time zone.

## What's new

- **FreeHire as a job source (#33):** choose FreeHire in Settings → Job search to search its open catalogue: millions of postings read straight from companies' own career pages (Greenhouse, Lever, Workday and many more), in most countries, each with its whole text. It needs no key and no account, and the job agent can search it too. It has no distance filter: the place you enter puts postings there first, so **Within** is off while FreeHire is chosen. If you run your own FreeHire, `DOSSIER_FREEHIRE_URL` points Dossier at it. FreeHire's free use is for your own job search; a Dossier you host for other people needs one of [its API plans](https://freehire.me/terms). Thanks to @navaneeth-algorithm, who sent it.
- **A line about the company or role (#15):** each role in Profile → Experience has **About the company or role**, for a line like "Series B fintech, 40 people" that would otherwise sit in the first bullet, where it reads as something you did. Resumes show it under the role's title, in italics, smaller and muted, in every theme. Tailoring never rewrites it, the AI reads it as background about the employer, and importing a resume reads it when the resume has such a line. Thanks to @VictoriaDom for asking for it.

## Fixes

- **Importing a resume with Anthropic (#24):** with Anthropic chosen, it always failed with "The AI provider ran into a problem": Anthropic refuses the shape of answer Dossier asks for as too complex, and trying again sent the same. Dossier now asks once more with that shape in the instructions instead, and remembers to for that model. Thanks to @berkayturk, who sent the fix (#16), and to @ruchindwd for reporting it.
- **DeepSeek's answers cut off (#34):** with DeepSeek's API as the OpenAI-compatible server, creating an application failed with "The model's answer was cut off before it was finished": DeepSeek's models think before they answer unless told not to, and the thinking used up the room left for the answer. Dossier now tells DeepSeek's API (any address at deepseek.com) to answer without thinking first. Other servers get the same requests as before. Thanks to @raghav-sec for reporting it.
- **"JSearch isn't answering right now" (#18):** JSearch searches Google for Jobs while it answers, which can take longer than the 10 seconds Dossier waited, outside the US especially, so Find jobs, Test connection and the job agent gave up. JSearch now gets 45 seconds; the other sources keep 10. Thanks to @berkayturk, who sent the fix.
- **"Invalid time zone specified: Etc/Unknown" (#24):** without `TZ` in `.env`, `compose.production.yml` gave the container an empty time zone, and the job agent's schedule, the Google sync and Settings failed with that error until a browser had told Dossier its own. `TZ` now defaults to UTC, and Dossier uses UTC whenever the server's time zone isn't one it knows. Thanks to @ruchindwd for reporting it.
- **`DOSSIER_JOBS_ARBEITSAGENTUR=off` with Compose:** `compose.production.yml` didn't pass it on, so the Bundesagentur für Arbeit stayed on offer. It does now, and passes on the new `DOSSIER_FREEHIRE_URL` too.
- **Buttons named by what they show:** for voice control, the theme thumbnails, an application's **Theme** menu and the next interview on the dashboard are now named by the words they show.
- **Updates:** Next.js 16.4, the Anthropic SDK 1.3 (Dossier still sends the key as `X-Api-Key`, for gateways that only read that), Ollama 0.40.10 in the compose file, and others.

## Upgrading

Take a backup, then deploy `ghcr.io/aientech/dossier:v1.10.0` (see [Upgrading](https://github.com/AienTech/dossier/wiki/Upgrading) in the wiki). If you use `compose.production.yml`, take the new one: it defaults `TZ` to UTC and passes on `DOSSIER_FREEHIRE_URL` and `DOSSIER_JOBS_ARBEITSAGENTUR`. On its first start, Dossier adds the line about the company or role to each role, empty, and FreeHire to the job sources; nothing else changes in your data or configuration.

Exports now include the line about the company or role, so a 1.10.0 export can't be imported into an older version. Exports from older versions import as before.
@AienTech

Copy link
Copy Markdown
Owner

Thanks, @berkayturk! I took this into Dossier with you as co-author, and it's out in 1.10.0. On the way in, the refusal is matched by the wordings seen and documented ("grammar is too large", "parameters with union types", "too complex"), so other 400s still fail as before. The fallback is remembered per model and schema, so other schemas on the same model keep structured output. Thanks also to @philorien for confirming it across models. Closing in favour of the release.

@AienTech AienTech closed this Oct 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants