DeepSeek Harness headless MISSING_CREDENTIAL: .env
The key sits in refs inside ~/.dsh/.credentials.yaml, with the real value inline, yet dsh --profile headless "hi" exits after a second with MISSING_CREDENTIAL: llm-pi-ai: no credential for provider route "zai-coding-cn". The key is not malformed; since 0.2.1-alpha.3 the bundled headless profile does not mount the credential service at all, and the whole source list changed (#11051). Identify the environment first, then the version, then pick a path.
DeepSeek Harness: Step 1: identify the environment — desktop working does not mean headless works
The desktop app reads the credential store; headless does not. The same key has completely different fates on the two sides, so confirm which one you are running.
- Look at the launch command:
--profile headless, or anything pulled up by a scheduler asdsh headless, is headless. - Look for a UI: the desktop app has a web UI and a "Models" settings page; headless has only stdout and NDJSON.
- Look at the error shape: headless prints
dsh: <CODE>: ...and exits on one line, while the desktop app shows a card in the UI. - If you run it in a scheduler or CI, treat it as headless by default.
This step has exactly one conclusion: do not carry over the desktop experience of "filling in a key on the models page is enough" to headless.
DSH plugin: Step 2: identify the version — 0.2.1-alpha.3 is the dividing line
This is a documented design change, not a regression: 0.2.0-rc.2 resolves stored credentials, and from 0.2.1-alpha.3 the bundled headless profile disables the related services.
- Run
dsh --versionand record the full version. - Below 0.2.1-alpha.3, investigate the old behavior first.
- At 0.2.1-alpha.3 and later, treat storage as not working and stop spending time validating the
.credentials.yamlformat. - Also read the profile's
cordis.patch.ymlonce; it explicitly lists credentials, authorization, and deepseek-account as disabled.
The community also observed that 0.2.1's first headless launch rewrites .credentials.yaml into the refs/records structure, so a changed file mtime does not mean you edited the right place.
DeepSeek Harness: The headless credential lookup order has only three places
Launch environment → the calling directory's .env → the Harness home .env, and nowhere else; the credential store is not among them.
- First priority is process environment variables, the
envat the moment you launch. - Second is
.envin the current working directory. - Third is
~/.dsh/.env(or the.envunderDSH_HOME). - Only when all three are empty does it throw
MISSING_CREDENTIAL, and that failure happens before contacting the model endpoint — so network, proxy, and balance are not the cause.
# ~/.dsh/.env (variable names follow the profile's refs)
ZAI_CODING_CN_API_KEY=sk-xxxxxxxx
The community ran an isolation comparison: headless with only a stored key sent no request at all, while exporting a fake key, the calling directory .env, the home .env, and the three-line patch all worked and the mock received the corresponding key. In the control group, SDK and ACP worked with only the stored key because their credential service is active.
DSH Plugin: Two workable paths: write .env or restore the services
The simplest is putting the key into ~/.dsh/.env; to keep using the credential store, add the services back with the README's three-line patch.
- Path A: write
ZAI_CODING_CN_API_KEY=<real key>into~/.dsh/.env, making sure the variable name matches the profile'srefsreference. - Tighten the file permissions; do not leave it world-readable.
- Run
dsh --profile headless "hi"again and see whetherMISSING_CREDENTIALis gone. - Path B: open
packages/bundle/headless/README.mdand add the documented three-line restore patch to your profile config. - Restart and run once more to verify credentials resolve from storage.
- If you use a scheduler such as LaunchAgent, put the key into its
EnvironmentVariablestoo, or interactive terminals will work while scheduled jobs keep failing.
In the community case a production cron failed twice at 07:30 and 09:00 on 10-23 and only recovered after the scheduler environment was injected.
DeepSeek Harness: The error message itself misleads you
The MISSING_CREDENTIAL text does not branch by plane: it first recommends "save it to the credential service (the web models page writes it there)", exactly the service headless does not mount.
- The source shows the resolver first calls
ctx.get('credentials')to decide whether to query storage or the launch environment. - But the error text is three hardcoded sentences that do not follow that branch.
- The result is a user following the first suggestion and hitting the same error again.
- What actually works is the third sentence, "export it in the launch environment" — the community notes it was always there, just buried by the first two.
- This is a diagnostic wording defect, low severity, but it is not "credentials lost" and carries no privilege risk.
DSH plugin: Side scenario: revoking browser session tokens globally
Browser cookies on the desktop web side survive ordinary restarts, so global revocation requires deleting the single client-connection/browser-session record and restarting.
- Fully stop every Harness process using the same home.
- Back up
.credentials.yaml(default%USERPROFILE%\.dsh\.credentials.yaml; use the actual path ifDSH_HOMEchanged). - Under
records, delete only theclient-connection/browser-sessionkey and its entire value, keepingversion,refs, and other records; if it was the last one, leaverecords: {}. - Restart Harness and sign in again with the freshly generated launch URL.
- Verify: an old cookie, without
?token=, against the same scheme/host/port should return 401. - A request with the new launch URL should return 303, and authentication with its new cookie should return 200.
One trap: the authority must match exactly. If you change hostname or port the cookie is invalid anyway, and you will mistakenly believe revocation worked. The community verified in an isolated environment that an old cookie still works after an ordinary restart, and is only rejected after that record is deleted and the app restarted.
Cautions
Check the plane and the version before editing any credential file.
- Do not edit only
.credentials.yamland expect headless to pick it up. It reads.envand environment variables. - Do not use SDK / ACP behavior as the reference. Their credential service is active, so the prompt applies to them.
- Do not ignore the scheduler environment. Working in an interactive terminal does not mean a cron job works.
- Do not delete the whole credential file to revoke a token. Delete only that one record; other references are unaffected.
- Record the version number. It is the only basis for deciding "design change or defect".
If your error code is 401/403 rather than MISSING_CREDENTIAL, that is a different chain — check the credential contents and account status first.See Fix DSH API key errors: MISSING_CREDENTIAL and UNKNOWN_MODEL for the related case.
FAQ
DeepSeek Harness never reads stored credentials under headless because the bundled headless profile disables the credentials, authorization, and deepseek-account services. It only checks the launch environment, the calling directory's .env, and ~/.dsh/.env, so putting the key in ~/.dsh/.env is what works.
DeepSeek Harness stopped reading the credential store from 0.2.1-alpha.3 according to the community reply, a documented change rather than a regression. The same config resolves fine on 0.2.0-rc.2, and after upgrading only the launch environment remains.
Saving the key to the credential service still fails because the DeepSeek Harness error text does not branch by plane. The code already checks with ctx.get('credentials') whether the service exists, but the text still recommends a service headless does not mount; the only useful line is the third one, export it in the launch environment.
The DeepSeek Harness fix is the three-line patch in packages/bundle/headless/README.md, which adds the credential services back without changing the profile. The community verified four working sources: exported environment variable, calling directory .env, ~/.dsh/.env, and the three-line restore patch.
To revoke browser session cookies globally, stop all DeepSeek Harness processes, back up .credentials.yaml, delete only client-connection/browser-session under records, and restart. An old cookie without ?token= against the same authority should then return 401.
Related Terms
- MISSING_CREDENTIAL
- MISSING_CREDENTIAL is the error code DeepSeek Harness returns when it cannot resolve model credentials, failing fast with exit 1 under headless. It means the credential source is unavailable in the current combination, not that the key itself is wrong.— https://github.com/deepseek-ai/deepseek-harness/discussions/11051
- headless profile
- The headless profile is the bundled profile for running DeepSeek Harness without a UI, whose cordis.patch.yml disables the credentials, authorization, and deepseek-account services by default. Credentials can only come from the launch environment or .env files.— https://github.com/deepseek-ai/deepseek-harness/discussions/10609
- launch environment
- The launch environment is the set of environment variables the dsh process inherits at startup, and it is the highest-priority credential source under headless. It ranks ahead of the calling directory's .env and the Harness home .env.— https://github.com/deepseek-ai/deepseek-harness/discussions/10609
- client-connection/browser-session
- client-connection/browser-session is the record key in the local credential store that holds a browser session token. Deleting that record and restarting revokes all issued session cookies without affecting other credential records.— https://github.com/deepseek-ai/deepseek-harness/discussions/10915
Sources
- #11051 — headless: credentials refs resolution broken (MISSING_CREDENTIAL)· deepseek-ai (GitHub Discussions)
- #10609 — fix(headless): missing-key diagnostic recommends an ignored credential store· deepseek-ai (GitHub Discussions)
- #10915 — Supported browser-session revocation procedure for Harness 0.2.1-rc.2· deepseek-ai (GitHub Discussions)
- deepseek-ai/deepseek-harness (source repository)· GitHub