DeepSeek Harness: Unknown agent preset after plugin disable
Disabling one rarely used plugin leaves DeepSeek Harness (DSH) able to start, yet no new session can be created. The disabled plugin used to provide a service, the preset row names that service in inject, and inject is a hard dependency in Cordis. The row does not fail; it waits in pending indefinitely. Boot never resolves a preset, so the host looks healthy until a new session tries to resolve one.
DeepSeek Harness symptoms: host boots, sessions fail
The split between a healthy host and broken session creation is the signature: boot does not need a preset, while a new session does. Compare the symptoms before touching anything:
| Action | Observed result | What it tells you |
|---|---|---|
| Start the host | succeeds | startup does not resolve presets |
| Open an existing session | usually succeeds | its preset is already fixed in the session header |
| Create a session | agent-preset/not-found: Unknown agent preset: standard | the preset row is not registered |
| Inspect the plugin list | pending (waiting for service: xxx) | an inject dependency is missing |
Then confirm the chain:
- Read the waiting service name — open the plugin list and find the row marked pending; expected: it names a specific service, not a generic failure.
- Find the provider — search the list for the plugin that offers that service; expected: it is currently disabled.
- Re-enable it temporarily — turn the provider back on and wait for active; expected: the new-session error disappears at once.
According to the source review in the discussion, vendor/cordis/src/registry.ts:71-88 normalizes inject without a required/optional distinction, so every key is treated as hard; app-boot/src/index.ts:323/331 defaults requiredIds to an empty array, and :399-405 checks requiredIds before excluding pending rows, leaving only a warning (#10099). For normal preset setup, see agent presets.
Why pending is silent and DSH sessions still fail
Pending is a wait, not a crash, and the wait is only visible at the moment a preset must be resolved. Trace it:
- Disable provider A — the service S it offered no longer exists, with no immediate error anywhere.
- Leave S in the preset row's inject — Cordis refuses to start that row until S appears, so the row shows pending.
- Start the host — only the base bundle is required, so startup succeeds without the preset row.
- Create a session — the runtime looks for
standardin the registry and reportsagent-preset/not-foundbecause the row never started.
That is why the failure looks like corrupted session data even though only a preset is missing.
Three DeepSeek Harness repair paths
Bottom line: re-enable the provider when you need a session now, patch inject when the plugin must stay off, and change the loader when you maintain a fork.
Path 1: re-enable the plugin that provides the service
This is the fastest recovery, and it means giving up the disablement; use it when the plugin was only switched off temporarily. Follow the provider chain:
- Note the missing service — read the pending row's
waiting for servicetext; expected: one concrete service name. - Locate its provider — search the plugin list for that service; expected: the provider is the disabled entry.
- Enable it — switch it on and wait for active; expected: the pending row starts.
- Create a session — choose the standard preset and send a message; expected: no
Unknown agent preset. - Fix a broken provider first if needed — if it fails to start, treat that as the real fault; expected: enabling a healthy provider clears both symptoms.
Path 2: blank or relax the preset row's inject
When the plugin must stay disabled and the preset does not truly need its service, override the row from the profile layer instead of editing the plugin.
- Open the patch file — edit
~/.dsh/profiles/<profile>/cordis.patch.yml; expected: you are in the profile layer, not the plugin source. - Identify the row — locate the preset row id and read its current
injectlist; expected: every hard dependency is visible. - Replace the list — add a patch for the same id with
injectset tonull; expected: the whole key is replaced, not merged. - Restart — relaunch DSH and inspect that row; expected: it no longer waits for a service.
- Create a session — resolve the preset and send a message; expected: the preset loads, and you can note any tool that disappeared.
Blanking inject removes every dependency on that row, including services the preset genuinely needs, so only use it when the row is thin. The patch replaces the block, so a partial rewrite can erase other fields; back up first. Row ids change across plugin upgrades, which silently invalidates the patch.
Path 3: teach the loader optional dependencies and timeouts
The durable fix is to let inject express required versus optional, and to degrade instead of waiting forever. This path suits a fork or an upstream contribution:
- Extend normalization — in
vendor/cordis/src/registry.ts, accept a shape such as{ required: [...], optional: [...] }; expected: the two kinds are distinguishable. - Stop blocking on optional — read optional dependencies with
get()and return undefined when absent; expected: the row starts without the provider. - Adjust the pending rule — in
app-boot/src/index.ts:399-405, treat a row whose only missing dependencies are optional as usable; expected: it becomes active. - Warn instead of silence — emit a warning that names the missing service; expected: the degradation is visible in the log.
- Test the disablement — disable the provider and create a session; expected: the preset still resolves with reduced capability.
Verify DSH recovery and avoid regressions
Do not stop at a single successful session; the preset must survive restarts and toggling. Check these points:
- Inspect the row — after restart, confirm it is no longer pending; expected: active, or gone if you removed it deliberately.
- Exercise the preset — create a session and send a message that invokes a tool; expected: the preset resolves and the tool runs.
- Open an old session — load one created before the change; expected: its existing tools still work.
- Toggle once more — disable and re-enable the provider; expected: the state changes cleanly with no leftover pending row.
Avoid these traps:
- Do not dismiss pending as harmless: it means a capability never loaded, and it usually surfaces later as a feature that quietly vanished.
requiredIdsdefaults to empty in the current implementation, so boot will not fail for you; monitor the pending list yourself.- Patch precedence follows file order, so rule out a second patch that restores
injectbefore debugging further. - No official fix version is named; 0.2.1-rc.2 still reproduces, so consult release notes and do not read an old DSH plugin pending state as current behavior.
DeepSeek Harness agent preset FAQ
Q: How do I fix DeepSeek Harness Unknown agent preset after disabling a plugin? A: DeepSeek Harness reports Unknown agent preset because the preset row lists a service from the disabled plugin in inject, and inject is a hard dependency in Cordis, so the row sits in pending. Startup does not resolve presets, but creating a session does; re-enable the plugin or blank that inject list.
Q: Why does a DeepSeek Harness plugin stay pending with no error in the log? A: A DeepSeek Harness plugin stays pending when its inject dependencies are not ready, typically because a provider is disabled, a version mismatched, or a dependency row failed. Unlike failed, pending does not block host startup, which is why it is easy to miss; read the service name and check its provider.
Q: What is the difference between inject and an optional dependency in DeepSeek Harness? A: DeepSeek Harness treats keys written into inject as hard dependencies; Cordis waits for them before starting the row. Optional capabilities should be read with get() or declared as optional services, because putting an optional tool into inject turns the whole row pending when it is disabled.
Q: Why is the DeepSeek Harness preset row still pending after setting inject: null? A: A DeepSeek Harness preset row can stay pending if the patch deep-merges instead of replacing the block, or if it targets the wrong row id. Check for a second patch that writes inject back, apply order matters, then clear the profile build cache and restart.
FAQ
DeepSeek Harness reports Unknown agent preset because the preset row lists a service from the disabled plugin in inject, and inject is a hard dependency in Cordis, so the row sits in pending. Startup does not resolve presets, but creating a session does; re-enable the plugin or blank that inject list.
A DeepSeek Harness plugin stays pending when its inject dependencies are not ready, typically because a provider is disabled, a version mismatched, or a dependency row failed. Unlike failed, pending does not block host startup, which is why it is easy to miss; read the service name and check its provider.
DeepSeek Harness treats keys written into inject as hard dependencies; Cordis waits for them before starting the row. Optional capabilities should be read with get() or declared as optional services, because putting an optional tool into inject turns the whole row pending when it is disabled.
A DeepSeek Harness preset row can stay pending if the patch deep-merges instead of replacing the block, or if it targets the wrong row id. Check for a second patch that writes inject back, apply order matters, then clear the profile build cache and restart.
Related Terms
- inject
- inject is the Cordis mechanism for declaring service dependencies. Names listed in inject are hard dependencies; a plugin waits in pending rather than erroring when they are missing. It suits required services, not optional capabilities.— https://github.com/deepseek-ai/deepseek-harness/discussions/10099
- agent preset
- agent preset is a named bundle of agent configuration in DeepSeek Harness, covering model, tools and persona. Creating a session requires resolving one; failure surfaces as agent-preset/not-found.— https://github.com/deepseek-ai/deepseek-harness/discussions/10099
- pending
- pending is a Cordis plugin state meaning inject dependencies are unmet. Unlike failed, pending does not block the host from starting, so it often appears as a working app with one missing capability.— https://github.com/deepseek-ai/deepseek-harness/discussions/10099
Sources
- #10099 — Loader inject has no optional or timeout fallback, so a preset row stays pending after a plugin is disabled· deepseek-ai (GitHub Discussions)
- DeepSeek Harness source repository· deepseek-ai (GitHub)