Troubleshooting landlock-run ENOENT in dsh Web Profile: The Problem Wasn’t Landlock After All

At first glance, the error looked straightforward.

While using the dsh Web profile, an error similar to the following appeared:

spawn .../landlock-run ENOENT

The obvious suspects were:

  • The landlock-run binary is missing
  • The systemd user session is broken
  • Sandbox configuration is incorrect
  • The Web profile behaves differently from the Headless profile

The investigation started from those assumptions.

After validating each layer one by one, the final result was unexpected. The current environment had a healthy Landlock setup, a functioning systemd user session, working sandbox components, and a correctly configured Web profile. The remaining task was no longer fixing Landlock. It was identifying which historical session had actually produced the ENOENT error.

Verify That the systemd User Environment Is Working

dsh launches isolated subprocesses through systemd user scopes.

The first step was confirming that the user-level systemd instance was healthy.

systemctl --user is-system-running

Output:

running

Next, verify the runtime directory and DBus session address.

echo "XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR"
echo "DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS"

Result:

XDG_RUNTIME_DIR=/run/user/0
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/0/bus

Both variables were already present and correctly populated.

To validate systemd user scopes directly, run:

systemd-run --user --scope --quiet --collect --expand-environment=no \
  --unit=dsh-test \
  -- /usr/bin/node -e 'console.log("SYSTEMD_RUN_OK")'

echo "exit=$?"

Output:

SYSTEMD_RUN_OK
exit=0

If this step fails, many later diagnostics become irrelevant because dsh relies on systemd user scopes for subprocess management.

At this stage, the systemd user environment was confirmed to be functioning correctly.


Test Subprocess Execution with the Headless Profile

The next step was isolating the problem from the Web profile entirely.

A simple Bash command was executed through the Headless profile.

dsh --profile headless \
  'printf "USER=%s\n" "$(whoami)";
   printf "SHELL=%s\n" "$SHELL";
   command -v bash;
   echo HELLO'

echo "exit=$?"

Output:

USER=root
SHELL=/bin/bash
/usr/bin/bash
HELLO

exit=0

This verified several important things:

Validation Result
Bash binary exists Success
User identity is valid root
Shell environment is available /bin/bash
Subprocess execution works Success

If landlock-run were actually missing, this test would usually fail immediately.


The Web Profile Behaves Differently

The same approach was then attempted against the Web profile.

dsh --profile web \
  'printf "USER=%s\n" "$(whoami)";
   printf "SHELL=%s\n" "$SHELL";
   command -v bash;
   echo WEB_TEST_OK'

Result:

error: too many arguments.
Expected 0 arguments but got 1

Another test produced the same outcome:

dsh --profile web \
  'id; /usr/bin/env bash -c "echo CHILD_BASH_OK; command -v bash; id"'

Output:

error: too many arguments.
Expected 0 arguments but got 1

At first this looked suspicious.

However, checking the help output clarified the behavior.

dsh --profile web --help

Output:

Usage: dsh --profile web [options]

Serve the DeepSeek Harness browser UI.

The Web profile is not designed as a command execution interface. It launches the browser-based UI service and does not accept shell commands as positional arguments.

That means these errors were unrelated to Landlock or sandboxing. They were simply command-line usage errors.


Export and Inspect the Full Web Profile Configuration

To determine whether the Web profile used a different sandbox implementation, the complete configuration was exported.

dsh --profile web --dump-config > /tmp/dsh-web-config.txt

Several key components appeared in the output:

- id: subprocess
  name: '@deepseek-ai/dsh-subprocess-local'

- id: sandbox
  name: '@deepseek-ai/dsh-sandbox-local'

- id: sandbox-policy
  name: '@deepseek-ai/dsh-sandbox-policy'

- id: bash-sandbox
  name: '@deepseek-ai/dsh-bash-sandbox'

The configuration also showed:

mode: workspace-write

And the available permission presets:

read-only
workspace-write
danger-full-access

From a sandbox perspective, the Web profile was using the same core components as the Headless profile:

dsh-subprocess-local
dsh-sandbox-local
dsh-bash-sandbox

No evidence suggested that the Web profile had switched to a different runner or isolation backend.


Check for Custom Profile Overrides

Next, inspect the profile files themselves.

sed -n '1,260p' /root/.dsh/profiles/web/cordis.yml

Result:

[]

Then inspect the patch layer:

sed -n '1,260p' /root/.dsh/profiles/web/cordis.patch.yml

Result:

[]

The Web profile had no user-defined overrides.

There was no custom patch replacing the default sandbox implementation.


Confirm the Active Web Service Process

The running dsh process was identified with:

pgrep -af dsh

Output:

800 node /usr/bin/dsh web --no-open --trusted-host kenfan0108.vip

To verify the exact launch command:

tr '\0' ' ' < /proc/800/cmdline

Output:

node /usr/bin/dsh web --no-open --trusted-host kenfan0108.vip

This confirmed that the active Web UI instance was running exactly as expected.


The Most Important Evidence Came from journalctl

The breakthrough came from reviewing systemd logs.

Search recent user journal entries:

journalctl --user --since "2 hours ago" --no-pager \
  | grep -Ei \
    'dsh|ENOENT|landlock|sandbox|runner|subprocess|bash'

Among the results were several successful subprocess launches.

Example:

Started dsh-subprocess-8634-9c10d71c9244.scope -
/usr/bin/node
/usr/lib/node_modules/@deepseek-ai/dsh/node_modules/@deepseek-ai/dsh-subprocess-local/lib/runner.js
--
/usr/lib/node_modules/@deepseek-ai/dsh/node_modules/@deepseek-ai/node-addon-system-linux-x64/bin/landlock-run
--
bash -c "echo HELLO"

Another example:

Started dsh-subprocess-8712-bcceb363dfa8.scope -
...
landlock-run
...
bash -c "printf \"USER=%s\n\" ..."

These log entries established several facts.

Fact 1: landlock-run Exists

The binary path appeared directly in systemd’s execution logs.

A missing executable would never reach this stage.

Fact 2: Node.js Can Spawn landlock-run

The subprocess runner successfully launched the binary.

That rules out the original assumption that Node could not locate the executable.

Fact 3: Landlock Is Applying Confinement Rules

The logs included arguments such as:

--ro /
--rw /tmp
--rw /root

Those confinement rules were successfully passed into the sandbox.

Fact 4: Bash Executes Successfully Inside the Sandbox

The Bash commands completed without failure.

This demonstrates that sandbox creation and command execution were both functioning.


What the Evidence Actually Proves

By the end of the investigation, the following components had been verified:

Landlock kernel support      OK
landlock-run binary          OK
Node spawn of landlock-run   OK
Landlock probe               OK
Landlock confinement         OK
systemd-run --user           OK
Headless profile             OK
Web profile sandbox config   OK
Web subprocess startup       OK

This changes the interpretation of the original error significantly.

The environment is clearly capable of creating the following chain successfully:

systemd scope
→ dsh-subprocess-local
→ landlock-run
→ bash

That makes it unlikely that the current system is suffering from a persistent Landlock failure.

A more plausible explanation is that a specific historical session, subagent execution, or tool invocation encountered a temporary ENOENT condition.


The Next Step Is Historical Log Analysis

At this point, modifying configuration files or reinstalling dsh would be premature.

The more productive direction is finding the original context where the ENOENT occurred.

Search all dsh data:

grep -RniE \
  'spawn .*landlock-run|landlock-run.*ENOENT|ENOENT' \
  /root/.dsh

Inspect session records:

find /root/.dsh/sessions \
  -type f \
  \( -name '*.jsonl' -o -name '*.log' -o -name '*.ndjson' -o -name '*.json' \)

And search subagent history:

grep -RniE \
  'ENOENT|landlock-run|node-addon-system-linux-x64' \
  /root/.dsh/subagent-gc-backup

The infrastructure itself has already been validated.

The remaining challenge is locating the exact session, subagent, or historical execution that originally generated the landlock-run ENOENT message.

Quick Diagnostic Checklist

  • Verify systemctl --user is-system-running
  • Check XDG_RUNTIME_DIR
  • Check DBUS_SESSION_BUS_ADDRESS
  • Test systemd-run --user
  • Validate Bash execution through the Headless profile
  • Export the Web profile configuration
  • Inspect cordis.patch.yml
  • Verify the active Web service process
  • Review subprocess startup logs in journalctl
  • Search session and subagent history for ENOENT records

FAQ

Why can’t the Web profile execute shell commands?

The Web profile is designed to launch the browser-based DeepSeek Harness UI. It does not accept shell commands as positional arguments.

Is the too many arguments error related to sandboxing?

No. The error occurs during command-line argument parsing before any sandbox component is involved.

Does landlock-run exist on the system?

Yes. Multiple systemd logs show the executable being launched successfully.

Can Node.js successfully spawn landlock-run?

Yes. The subprocess runner starts it repeatedly without failure.

Is the Headless profile functioning correctly?

Yes. Bash commands execute successfully and return expected output.

Does the Web profile use a different sandbox implementation?

Based on the exported configuration, it uses the same core sandbox components as the Headless profile.

Should dsh be reinstalled?

Current evidence does not support a reinstall.

What is the most useful next troubleshooting step?

Search historical session logs, subagent backups, and dsh records to identify the original context that produced the ENOENT error.