Skip to content

fix(cli): ensure store directory exists in createFileWithStore (#4286) - #4966

Closed
kaiizer777 wants to merge 1 commit into
triggerdotdev:mainfrom
kaiizer777:fix/cli-dev-store-dir-enoent
Closed

kaiizer777 wants to merge 1 commit into
triggerdotdev:mainfrom
kaiizer777:fix/cli-dev-store-dir-enoent

Conversation

@kaiizer777

Copy link
Copy Markdown

Resolves #4286

Root Cause

In packages/cli-v3/src/utilities/tempDirectories.ts, getStoreDir() creates .trigger/tmp/store when a dev session starts and registers an exit cleanup handler that deletes it recursively.
When a developer starts a second trigger dev session for the same project, the previous dev session exits and its cleanup handler wipes out .trigger/tmp/store out from under the new session.
In packages/cli-v3/src/utilities/fileSystem.ts, createFileWithStore() ensured the build destination directory exists via await fsModule.mkdir(pathModule.dirname(filePath), { recursive: true });, but never ensured storeDir exists before writing the hashed artifact via await fsModule.writeFile(storePath, contents);.
When storeDir has been deleted by the exiting session, createFileWithStore() threw an unhandled ENOENT: no such file or directory, crashing the build or causing watch rebuilds to stall.

Changes

  • In packages/cli-v3/src/utilities/fileSystem.ts, added await fsModule.mkdir(storeDir, { recursive: true }); inside createFileWithStore() alongside the destination directory check.
  • Added comprehensive unit tests in packages/cli-v3/src/utilities/fileSystem.test.ts verifying that:
    • createFileWithStore() succeeds when storeDir does not exist yet (creates it and writes file).
    • Re-running when storeDir exists uses content-addressable caching (hardlink or copy).
    • Existing destination files are overwritten cleanly.
    • Base64 hashes with / and + characters are properly sanitized.
  • Added a patch changeset targeting trigger.dev.

Note

A vouch request for CI/contributions is open at #4963.

@changeset-bot

changeset-bot Bot commented Sep 20, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8f4da02

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 27 packages
Name Type
trigger.dev Patch
@internal/dashboard-agent Patch
@trigger.dev/build Patch
@trigger.dev/core Patch
@trigger.dev/python Patch
@trigger.dev/react-hooks Patch
@trigger.dev/redis-worker Patch
@trigger.dev/rsc Patch
@trigger.dev/schema-to-json Patch
@trigger.dev/sdk Patch
@trigger.dev/database Patch
@trigger.dev/otlp-importer Patch
@trigger.dev/rbac Patch
@trigger.dev/sso Patch
@internal/clickhouse Patch
@internal/llm-model-catalog Patch
@internal/metrics-pipeline Patch
@internal/redis Patch
@internal/replication Patch
@internal/run-engine Patch
@internal/run-store Patch
@internal/schedule-engine Patch
@internal/tracing Patch
@internal/webhook-engine Patch
@internal/webhook-sources Patch
@internal/testcontainers Patch
@internal/cache Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

Copy link
Copy Markdown
Contributor

Hi @kaiizer777, thanks for your interest in contributing!

This project requires that pull request authors are vouched, and you are not in the list of vouched users.

This PR will be closed automatically. See https://github.com/triggerdotdev/trigger.dev/blob/main/CONTRIBUTING.md for more details.

@github-actions github-actions Bot closed this Sep 20, 2026
@kaiizer777

Copy link
Copy Markdown
Author

Vouch request is open at #4963. Ready for review once vouched!

@coderabbitai

coderabbitai Bot commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: f9329ebd-3da8-448c-b74a-d59ceb4b8c92

📥 Commits

Reviewing files that changed from the base of the PR and between 0d3e54d and 8f4da02.

📒 Files selected for processing (3)
  • .changeset/cli-dev-store-dir-enoent.md
  • packages/cli-v3/src/utilities/fileSystem.test.ts
  • packages/cli-v3/src/utilities/fileSystem.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Devin Review


// Ensure build directory exists
// Ensure build directory and store directory exist
await fsModule.mkdir(storeDir, { recursive: true });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Concurrent cleanup still deletes store

When the previous session cleans up after mkdir, createFileWithStore still reaches a missing store. The new dev session's build still crashes during handover.

Learn more

The store is shared by dev sessions for the same project and branch. Each session registers an exit callback that recursively removes that shared directory in getStoreDir. Creating it once at the start of this function does not establish ownership or synchronize with that callback. The old callback can run immediately after mkdir, after the existence check, or after the store write. A later store operation then raises ENOENT, preserving the original handover failure.

Example: Session B executes mkdir(storeDir) and pauses. Session A exits and recursively removes storeDir. Session B then executes writeFile(storePath, contents) and receives ENOENT instead of completing its rebuild.

Recommended fix: Stop an exiting session from deleting a store that another session uses. Give stores session-specific ownership, coordinate shared cleanup with a lock or reference count, or retain the shared store and clean stale data only when no session can use it. Add a handover test that deletes the store after createFileWithStore begins.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

"trigger.dev": patch
---

Ensure store directory exists before writing in createFileWithStore to prevent ENOENT crashes during dev session handover.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Release note exposes internals

The changeset names createFileWithStore, its store directory, and ENOENT. Release notes must describe the user-visible dev-session fix instead.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

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.

bug: starting a second trigger dev session can crash it or silently break rebuilds

1 participant