openclaw - 💡(How to fix) Fix Skill registry: changes to skills.load.extraDirs do not propagate to existing per-session skillsSnapshot

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Adding a new root to skills.load.extraDirs in ~/.openclaw/openclaw.json makes the skill visible in openclaw skills list --agent <agent> but it never reaches the per-session --plugin-dir mount that gets passed to claude. Existing sessions keep using their cached skillsSnapshot and never pick the new extraDirs roots up. Restarting Gateway / opening a fresh session is required as a workaround.

Root Cause

dist/refresh-*.js :: ensureSkillsWatcher(...) rebuilds its chokidar watcher when watchTargets change (set of paths derived from extraDirs + plugin skill dirs), but it does NOT call bumpSkillsSnapshotVersion(...) in that branch. The per-session reuse gate at dist/session-updates-*.js :: shouldRefreshSnapshotForVersion(...) only compares snapshot versions, so existing sessions keep their stale skillsSnapshot. Subsequent prepareClaudeCliSkillsPlugin(...) only materializes skills present in that stale snapshot.

(File suffixes elided since they are per-release content hashes.)

Fix Action

Workaround

Manually trigger a fresh session after editing extraDirs, or restart Gateway, so the new sessions rebuild skillsSnapshot from scratch.

Code Example

const watchTargets = resolveWatchTargets(workspaceDir, params.config);
   const pathsKey = watchTargets.join("|");
+  const watchTargetsChanged = Boolean(existing) && existing.pathsKey !== pathsKey;
   if (existing && existing.pathsKey === pathsKey && existing.debounceMs === debounceMs) return;
   ...
   watchers.set(workspaceDir, state);
+  if (watchTargetsChanged) {
+    bumpSkillsSnapshotVersion({
+      workspaceDir,
+      reason: "watch-targets",
+      changedPath: pathsKey,
+    });
+  }
 }
RAW_BUFFERClick to expand / collapse

Summary

Adding a new root to skills.load.extraDirs in ~/.openclaw/openclaw.json makes the skill visible in openclaw skills list --agent <agent> but it never reaches the per-session --plugin-dir mount that gets passed to claude. Existing sessions keep using their cached skillsSnapshot and never pick the new extraDirs roots up. Restarting Gateway / opening a fresh session is required as a workaround.

Version

[email protected] (npm-managed install).

Reproduce

  1. With an open OpenClaw agent session, add a new root to skills.load.extraDirs, e.g. "skills": { "load": { "extraDirs": ["/some/shared/skills"] } }
  2. Place a valid SKILL.md under /some/shared/skills/example-skill/.
  3. Confirm registry sees it: openclaw skills list --agent <agent> --json shows the skill with source: "openclaw-extra".
  4. Inspect the live plugin-dir handed to claude: ls /tmp/openclaw/openclaw-claude-skills-*/openclaw-skills/skills/ The new skill is NOT present, even though plugin-generated extras with the same source: "openclaw-extra" label (e.g. browser-automation) ARE present.

Root cause

dist/refresh-*.js :: ensureSkillsWatcher(...) rebuilds its chokidar watcher when watchTargets change (set of paths derived from extraDirs + plugin skill dirs), but it does NOT call bumpSkillsSnapshotVersion(...) in that branch. The per-session reuse gate at dist/session-updates-*.js :: shouldRefreshSnapshotForVersion(...) only compares snapshot versions, so existing sessions keep their stale skillsSnapshot. Subsequent prepareClaudeCliSkillsPlugin(...) only materializes skills present in that stale snapshot.

(File suffixes elided since they are per-release content hashes.)

Proposed fix

When ensureSkillsWatcher detects that pathsKey (the joined watch targets) has changed vs the previous watcher, bump the snapshot version once, with reason: "watch-targets". Minimal diff (logical, against dist/refresh-*.js :: ensureSkillsWatcher):

   const watchTargets = resolveWatchTargets(workspaceDir, params.config);
   const pathsKey = watchTargets.join("|");
+  const watchTargetsChanged = Boolean(existing) && existing.pathsKey !== pathsKey;
   if (existing && existing.pathsKey === pathsKey && existing.debounceMs === debounceMs) return;
   ...
   watchers.set(workspaceDir, state);
+  if (watchTargetsChanged) {
+    bumpSkillsSnapshotVersion({
+      workspaceDir,
+      reason: "watch-targets",
+      changedPath: pathsKey,
+    });
+  }
 }

This is the narrowest fix: it doesn't broaden which sources may mount, it only invalidates stale per-session snapshots when the set of watched skill roots changes. SKILL.md file changes already bump via the existing watcher events; this adds the missing equivalent for root-set changes from config or plugin-skill path changes.

Workaround

Manually trigger a fresh session after editing extraDirs, or restart Gateway, so the new sessions rebuild skillsSnapshot from scratch.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - 💡(How to fix) Fix Skill registry: changes to skills.load.extraDirs do not propagate to existing per-session skillsSnapshot