fix: handle ENOTDIR in FileDataAccessor.getStats to prevent 500 on duplicate slug POST - #2152
Merged
joachimvh merged 1 commit intoApr 21, 2026
Conversation
timgent
marked this pull request as draft
April 17, 2026 21:42
timgent
marked this pull request as ready for review
April 18, 2026 17:23
timgent
force-pushed
the
fix-save-file-twice-bug
branch
from
April 18, 2026 17:33
5cd43f6 to
2dc6306
Compare
joachimvh
requested changes
Apr 20, 2026
joachimvh
left a comment
Member
There was a problem hiding this comment.
Nice find! Can you just remove the RELEASE_NOTES changes from the PR. That file is more for the drastic changes, minor bugfixes get added to the automatically generated changelog.
…plicate slug When a file already exists at a given path, stat()-ing that path with a trailing slash (e.g. test.json/) causes the OS to return ENOTDIR instead of ENOENT. This error was not caught by getStats, causing it to propagate as a raw OS error and result in a 500 InternalServerError on the second POST with the same Slug header to a file-system-backed server. Treating ENOTDIR the same as ENOENT (i.e. converting it to NotFoundHttpError) means hasResource correctly returns false for the container-style path check, allowing createSafeUri to fall back to a UUID-based name for the duplicate upload. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
timgent
force-pushed
the
fix-save-file-twice-bug
branch
from
April 20, 2026 17:32
2dc6306 to
56688a4
Compare
Contributor
Author
|
@joachimvh I've removed the update to the release notes - sorry about that! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📁 Related issues
#2021
✍️ Description
Patch fix (I don't have permissions to add the label)
Posting a file to a container twice with the same
Slugheader causes a500 InternalServerErroron file-system-backed deployments (e.g.solidcommunity.net). The second POST succeeds on in-memory servers but crashes on disk-based ones.Root cause:
DataAccessorBasedStore.createSafeUrichecks whether a resource already exists at the slug path by callinghasResourcewith bothtest.jsonand its container equivalenttest.json/. On Linux/macOS,stat('test.json/')on a regular file returns the POSIX errorENOTDIRinstead ofENOENT.FileDataAccessor.getStatsonly caughtENOENTand converted it toNotFoundHttpError;ENOTDIRescaped as a raw OS error, whichParsingHttpHandlerthen wrapped into a 500.Call trace (before fix):
POST /container/withSlug: test.json(file already exists on disk)DataAccessorBasedStore.addResource→createSafeUricreateSafeUribuilds candidate pathtest.json, then checkshasResource({ path: 'test.json/' })first (the container-equivalent check)hasResourcecallsFileDataAccessor.getMetadata({ path: 'test.json/' })getMetadatamaps the URL to a filesystem path…/test.json/and callsgetStatsgetStatscallsstat('…/test.json/')— Linux sees the trailing slash, tries to treattest.jsonas a directory, and throwsENOTDIRgetStatsonly handlesENOENT;ENOTDIRis re-thrown as a raw OS errorhasResourceonly catchesNotFoundHttpError; rawENOTDIRpropagates out — the||short-circuits and the second check (test.jsonwithout slash) is never reachedParsingHttpHandler.handleErrorsees a non-HttpErrorand returns a 500Fix: Also catch
ENOTDIRingetStatsand convert it toNotFoundHttpError.ENOTDIRon a trailing-slash path means "no container exists here" — semantically identical to not-found. With the fix:hasResource({ path: 'test.json/' })catchesNotFoundHttpError→ returnsfalsefalse ||continues tohasResource({ path: 'test.json' })→ file exists → returnstrue201Result after fix:
Slug: test.json→201at.../test.json201at.../cb0ccbd8-f25a-...(UUID fallback)✅ PR check list
Before this pull request can be merged, a core maintainer will check whether