Skip to content

fix(skills): stop skills updater throwing ENOENT on removed staging dir#3236

Draft
posthog[bot] wants to merge 1 commit into
mainfrom
posthog-code/fix-skills-updater-enoent
Draft

fix(skills): stop skills updater throwing ENOENT on removed staging dir#3236
posthog[bot] wants to merge 1 commit into
mainfrom
posthog-code/fix-skills-updater-enoent

Conversation

@posthog

@posthog posthog Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Problem

The desktop app's skills auto-updater (PosthogPluginService) staged downloads into a shared <appData>/skills.new directory and then called readdir on it to validate the download — with no ENOENT guard and no per-run isolation. The updating flag only serializes runs within a single process, so a second workspace-server instance (another app window/project) — or the atomic swap rename — could delete skills.new out from under an in-flight run, and the readdir blew up with ENOENT ... scandir '.../skills.new'.

The failure is non-fatal (the updater retries on the next interval and bundled skills keep working), so the real cost was error-tracking noise from an exception that shouldn't be surfaced.

Changes

  • Isolate staging per instance. The staging and backup dirs are now <skills>.new-<runId> / <skills>.old-<runId>, keyed on a stable per-process runId (process.pid). Concurrent instances no longer share a path, so they can't clobber each other's staging dir. The dirs stay on the same filesystem as the runtime skills dir, so the atomic-swap rename still works, and the stable pid lets the create-staging-dir step self-heal any leftover dir from an interrupted prior run.
  • ENOENT-tolerant reads/swap (defense in depth). A missing staging dir is now treated as an empty, no-op cycle (skip the swap, retry next interval) rather than a thrown ENOENT — at both the validate-skills readdir and just before the swap rename.

How did you test this?

  • Added a regression test that simulates a concurrent instance removing the staging dir mid-run and asserts the cycle is a silent no-op (no captureException, existing cache preserved, no false "updated" event) — it fails without the fix.
  • Added a test asserting the shared .new path is never used and the per-instance staging/backup dirs are cleaned up on success.
  • pnpm --filter @posthog/workspace-server test posthog-plugin → 40 passing.
  • Biome lint clean on the changed files; typecheck clean for the changed files.

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

Created with PostHog Code from an inbox report.

The skills auto-updater staged downloads into a shared `<skills>.new`
directory and called `readdir` on it with no ENOENT guard. A second
workspace-server instance (another window/project) — or the atomic swap
`rename` — could remove that shared path mid-run, so `readdir` threw
`ENOENT ... scandir '.../skills.new'`, which surfaced as error-tracking
noise even though the failure is non-fatal (the updater retries on the
next interval and bundled skills keep working).

Isolate the staging (and backup) dirs per instance via a stable
`runId` (`process.pid`) so a concurrent instance can no longer clobber
the path, keeping them on the same filesystem so the atomic-swap
`rename` still works. As defense in depth, treat a missing staging dir
as an empty/no-op cycle rather than an ENOENT throw at both the validate
`readdir` and the swap.

Generated-By: PostHog Code
Task-Id: 905f1443-bcf1-47a5-b6b8-c7b6cf65ad7e
@trunk-io

trunk-io Bot commented Jul 7, 2026

Copy link
Copy Markdown

Merging to main in this repository is managed by Trunk.

  • To merge this pull request, check the box to the left or comment /trunk merge below.

After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

React Doctor found no issues in the changed files. 🎉

Reviewed by React Doctor for commit 86d6003.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants