Skip to content

Plugin-skill registration uses fs.symlink (EPERM on Windows without admin/Developer Mode); junction would work without elevation #77958

@jvpflum

Description

@jvpflum

Version

  • openclaw 2026.5.4 (325df3e)
  • Windows 11 (build 26200), Node.js (via nvm4w), PowerShell 7
  • Standard user account; Developer Mode is OFF

Summary

Plugin-skill registration uses fs.symlink to expose plugin-shipped skills under ~/.openclaw/plugin-skills/. On Windows, creating a fs.symlink requires either administrator elevation or Windows Developer Mode. For users with neither (the default), every plugin-shipped skill silently fails to register with EPERM: operation not permitted, symlink ....

In my install, this leaves at least three skills undiscovered: browser-automation (from @openclaw/browser-plugin), obsidian-vault-maintainer, and wiki-maintainer (both from @openclaw/memory-wiki). The errors also flood stderr on every openclaw doctor invocation — but the worse impact is that openclaw skills list doesn't show them at all, so users can't easily tell why an installed plugin's skills aren't available.

Reproduction

Fresh install on a standard Windows user account (no Developer Mode, no admin):

> openclaw doctor
...
[skills] failed to create plugin skill symlink "C:\Users\jarro\.openclaw\plugin-skills\browser-automation""C:\Users\jarro\AppData\Roaming\npm\node_modules\openclaw\dist\extensions\browser\skills\browser-automation": Error: EPERM: operation not permitted, symlink 'C:\Users\jarro\AppData\Roaming\npm\node_modules\openclaw\dist\extensions\browser\skills\browser-automation' -> 'C:\Users\jarro\.openclaw\plugin-skills\browser-automation'
[skills] failed to create plugin skill symlink "C:\Users\jarro\.openclaw\plugin-skills\obsidian-vault-maintainer""C:\Users\jarro\AppData\Roaming\npm\node_modules\openclaw\dist\extensions\memory-wiki\skills\obsidian-vault-maintainer": Error: EPERM: operation not permitted, symlink ...
[skills] failed to create plugin skill symlink "C:\Users\jarro\.openclaw\plugin-skills\wiki-maintainer""C:\Users\jarro\AppData\Roaming\npm\node_modules\openclaw\dist\extensions\memory-wiki\skills\wiki-maintainer": Error: EPERM: operation not permitted, symlink ...

The same errors print on every CLI invocation that touches plugin-skill registration.

Why a plain fs.symlink fallback is the wrong fix on Windows

On NTFS, directory junctions (reparse points) behave the same as symlinks for fs.readdir/fs.realpath and do not require elevation or Developer Mode. The skill folders OpenClaw is trying to expose are all directories rooted under the npm module dir, which is exactly the case junctions handle.

I confirmed locally that the same target the symlink call is failing on works fine via a junction:

> $src = "$env:APPDATA\npm\node_modules\openclaw\dist\extensions\browser\skills\browser-automation"
> $dst = "$env:TEMP\junction-test"
> cmd /c "mklink /J `"$dst`" `"$src`""
Junction created for C:\Users\jarro\AppData\Local\Temp\junction-test <<===>> C:\Users\jarro\AppData\Roaming\npm\node_modules\openclaw\dist\extensions\browser\skills\browser-automation

(No admin, no Developer Mode.)

Suggested fix

In the plugin-skill registration helper, on process.platform === "win32" and only for directory targets, fall back to a junction when fs.symlink throws EPERM:

try {
  await fs.symlink(target, link, "junction"); // Node already supports "junction" type on Windows
} catch (err) {
  if (process.platform === "win32" && (err as NodeJS.ErrnoException).code === "EPERM") {
    // last-resort: spawn `cmd /c mklink /J` if the junction type didn't take
    await spawnMklinkJunction(link, target);
  } else {
    throw err;
  }
}

Node's own fs.symlink(target, path, "junction") on Windows already maps to a directory junction without requiring elevation, so depending on what's currently being passed, switching the type argument from default/"dir" to "junction" may be the entire fix. Worth confirming the call site is using the default/"dir" mode (which uses NT symlinks and triggers the EPERM).

If a junction is genuinely undesirable (e.g. the skill target is a file, not a directory), fall back to a hard copy — slower but guaranteed to work — and surface a one-line warning telling the user how to enable Developer Mode if they want symlink behavior back.

Impact

Three "stock" plugin-shipped skills are silently unavailable on stock Windows installs:

  • browser-automation — blocks the entire @openclaw/browser-plugin skill surface
  • obsidian-vault-maintainer
  • wiki-maintainer

I ship a desktop frontend on top of OpenClaw (Crystal: https://github.com/jvpflum/Crystal) and was tracking down "why does my browser skill list show empty" when I found this in openclaw doctor. The error is logged but never bubbles up to the skills surface, so users hit it as an "I installed the plugin but the skill isn't there" mystery.

Out of scope but related

The EPERM error itself is also printed to stderr on every CLI invocation that touches skill registration, including --json reads. That's the same anti-pattern flagged in #77942 (config-validator output leaking into machine-readable command stderr). Mentioning here for context but not asking for it to be fixed in this issue.

Happy to send a focused PR + test once you confirm the preferred call-site change (switch fs.symlink type to "junction" vs add explicit fallback logic).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions