UNKNOWN_MODEL: Unknown model — the exact model ids Genace accepts
{
"error": {
"type": "model_unavailable",
"code": "UNKNOWN_MODEL",
"message": "Unknown model: veo-3",
"param": "model"
}
}
The model string matched no registered provider. The message echoes what you
sent, so the typo is usually visible in the error itself.
The accepted ids
Model ids are exact strings, not display names:
| Modality | Model id | Display name |
|---|---|---|
| video | seedance-2.0-fast | Seedance 2.0 Fast |
| video | kling-2.0-standard | Kling 2.0 Standard |
| video | veo3 | Veo 3 |
| image | nano-banana | Nano Banana |
| image | flux-pro | Flux Pro v1.1 |
| image | ideogram-v3 | Ideogram v3 |
Two that catch people out:
veo3, with no hyphen. Notveo-3.- Version numbers use dots.
seedance-2.0-fast, notseedance-2-0-fast.
Omitting the field
model is optional. Leave it out and you get the default for that endpoint:
- video →
seedance-2.0-fast - image →
nano-banana
Both defaults are the cheapest option in their modality. That is deliberate — an unspecified model should not be the expensive one.
Listing them at runtime
Rather than hard-coding the table above, GET /v1/models returns the current
registry. It needs authentication but costs nothing and touches no quota,
which also makes it a convenient way to verify an API key — see
INVALID_API_KEY.
This used to be a 500
Like INVALID_PARAM, an unknown model previously threw
a plain error and surfaced as 500 internal server error, hiding the fact that
the caller simply had a typo. It now returns 400 with the offending value.
Where these facts come from
- codebase: src/ai/jobs/service.ts — provider lookup in submitJob
- codebase: src/ai/providers/registry.ts — ALL and DEFAULTS