Fix DeepSeek Harness Model Errors: API Key & Custom Gateway
When a DeepSeek Harness model errors, read the error keyword before doing anything: MISSING_CREDENTIAL and 401 point to the API key, UNKNOWN_MODEL points to model config, and image rejection or gateway failure points to input and compat in settings.yaml. Match the keyword in the quick table below, then follow the corresponding item.
Overview: read the error keyword first
Match the error keyword to its root cause before touching anything — different keywords mean completely different problems. The table below lists every error from the official troubleshooting section (source):
| Error keyword | Most likely cause | Jump to |
|---|---|---|
MISSING_CREDENTIAL | API key not saved, or env var not injected | MISSING_CREDENTIAL |
| Fetch available models 401 | Wrong key, or the service has no GET /models endpoint | Fetch 401 |
UNKNOWN_MODEL | Model is not among the configured providers | UNKNOWN_MODEL |
| Image rejected before sending | Model has not declared image modality (input) | Image rejected |
| Provider rejects image request | Declared an image capability the endpoint does not provide | Image request rejected |
| Custom gateway fails / reasoning content wrong | Reasoning format mis-guessed from the URL (compat) | Reasoning format |
Each error below is an item with error / cause / fix. DSH is still in developer preview and config fields follow the official docs; model config changes take effect on the next request without a restart (source).
Class 1: API key errors (MISSING_CREDENTIAL, 401)
MISSING_CREDENTIAL
- Error: connection fails with
MISSING_CREDENTIAL. - Cause: the key is not in place — either it was not saved on the model page, or the environment variable was not injected. The key itself never lives in settings.yaml.
- Fix:
- Open Settings → Models, enter the API key on the provider card and save. The key is stored in
$DSH_HOME/.credentials.yamland the page only receives a masked descriptor, never the plaintext key (source). - Or reference an environment variable name with
apiKeyEnvin settings.yaml (not the key itself):
yamlllm-pi-ai: providers: my-gateway: apiKeyEnv: GATEWAY_API_KEY # the env var name, not the key api: openai-completions baseURL: https://gateway.example/v1 models: - id: my-model- Export the variable before starting:
export GATEWAY_API_KEY="sk-...", then run DSH. Changes take effect on the next request — no restart (source).
- Open Settings → Models, enter the API key on the provider card and save. The key is stored in
Fetch available models returns 401
- Error: Settings → Models → Fetch available models returns 401.
- Cause: model discovery calls the OpenAI-compatible
GET /modelsendpoint, which many gateways do not expose (source). - Fix:
- Confirm the key itself is correct.
- If the service has no
GET /modelsendpoint, enter model ids manually inmodels— the ids must match the provider's actual model field exactly.
Class 2: Model config errors (UNKNOWN_MODEL)
UNKNOWN_MODEL
- Error: requests fail with
UNKNOWN_MODEL, or the model selector shows Select model and blocks input. - Cause: the requested model is not among the configured providers (source). Two typical cases:
- a saved session default points to a deleted provider;
- the custom provider's
modelslist lacks this model id.
- Fix:
- Case 1: re-select an already configured model, or re-add the deleted provider under the same id.
- Case 2: open
$DSH_HOME/settings.yamland append the id to that provider'smodelsblock:
yamlmodels: - id: my-model - id: my-model-2 # append this line- If you suspect the config was not written, run
dsh --dump-configand check the model rows in the merged config tree before rebuilding the provider.
Provider IDs are permanent: requests, saved sessions, model defaults, and credential references all use them, so renaming one breaks every reference; the official way to rename is to add a new provider and delete the old one (source).
Class 3: Custom gateway compatibility errors (image modality, compat reasoning format)
This is the most confusing bucket: the key and Base URL are correct, yet the gateway rejects every request. The root cause is almost always config that does not declare what the model supports — undeclared models are treated as text-only (source).
Image rejected before sending
- Error: attaching an image is rejected outright, naming the model as not supporting images.
- Cause: manually entered models default to text-only — nothing can interrogate the endpoint for its modalities (source).
- Fix: declare image modality for the model in settings.yaml:
If every model on a route accepts images, setyaml
llm-pi-ai: providers: my-gateway: apiKeyEnv: GATEWAY_API_KEY api: openai-completions baseURL: https://gateway.example/v1 models: - id: vision-model input: [text, image] # declare image supportdefaultInput: [text, image]once at the route as a fallback (the default is[text]); unknown modalities written anywhere are rejected (source). - Note: DeepSeek's own chat-completions route is text-only, and declaring
inputdoes not make it accept images — the provider rejects the request (source).
Provider rejects the image request
- Error: the provider rejects the request, saying images are not supported by the endpoint.
- Cause: you declared an image capability the endpoint does not actually provide — either in the model's
inputor the route'sdefaultInput. - Fix:
- Remove
imagefrom the list that granted it. - Start a new session: attached images stay in the session log, so an old session keeps repeating the same request until it leaves (source).
- Remove
Private gateway reasoning format (compat)
- Error: key and baseURL are correct, but the custom gateway call fails, reasoning content is missing, or the model does not think the way it should.
- Cause: pi-ai guesses the reasoning dialect from the endpoint URL (
reasoning_effort, DeepSeek'sthinking, z.ai'sthinkingobject, etc.), and a private gateway's URL says nothing, so it is spoken to in the OpenAI dialect by default (source). - Fix: declare
compaton the route or the model (effective only on theopenai-completionsprotocol; the model level overrides the route level):Onlyyamlcompat: thinkingFormat: deepseek # tell the adapter this is a DeepSeek reasoning dialectcompat.thinkingFormatandcompat.supportsReasoningEffortare configurable. Other compat fields (such asmaxTokensFieldandsupportsStore) stay auto-detected and are deliberately not configurable — do not hard-code them.
General debugging flow
Run any model error through this order — most issues are found in the first three steps.
- Read the error keyword → match it in the quick table to locate the root-cause type.
- Check the declared config → is the key saved/injected, is the model id in the models list, are image modality and reasoning format declared?
- Confirm the version and docs → DSH is in developer preview; follow the official docs for field names.
- Use plugins as an aid → to see at a glance whether each call really goes through, pick model-related DSH plugins (token usage, cost tracking) from the model category in DSH Plugin Hub.
Notes
- Model config changes in settings.yaml take effect on the next request — no restart.
apiKeyEnvtakes an environment variable name, not the key itself; prefer saving keys in Settings → Models.- Manually entered model ids must match the provider's model field exactly, or you get
UNKNOWN_MODEL. - Declare image modality with
input, but never declare a modality the endpoint does not provide. - DeepSeek's official chat-completions route is text-only; declaring images does not make it accept them.
- Let the error keyword steer you:
MISSING_CREDENTIAL/401 → key,UNKNOWN_MODEL→ model config, image rejection/request failure →inputandcompat.
Source: Configure models (official docs), dsh-llm-pi-ai README
FAQ
The key is not in place: save the API key for that provider in Settings → Models (stored in $DSH_HOME/.credentials.yaml), or reference an environment variable name with apiKeyEnv in settings.yaml and export it before starting.
The requested model is not among the configured providers: pick an already configured model, or append the missing model id to that provider's models block in settings.yaml. Provider IDs are permanent — rename by adding a new provider and deleting the old one, or old session references break.
Manually entered models are treated as text-only until declared. Add input: [text, image] to the model, or set defaultInput: [text, image] at the route as a fallback. DeepSeek's own chat-completions route is text-only and cannot be changed via config.
Not necessarily. Model discovery calls the OpenAI-compatible GET /models endpoint, and a service that does not expose it returns 401. Check the key first; if the endpoint is missing, enter model ids manually in the models block.
Check the declared config: whether the model declares image modality (input) and whether the gateway's reasoning format is mis-guessed from the URL (compat.thinkingFormat). Changes take effect on the next request — no restart.
Sources
- DeepSeek Harness documentation - Configure models (with troubleshooting)· deepseek-harness
- @deepseek-ai/dsh-llm-pi-ai README· deepseek-ai