[PR #5203] fix for unhandled error on eacces #4461

Open
opened 2026-04-25 00:51:04 +02:00 by adam · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/advplyr/audiobookshelf/pull/5203
Author: @rsJames-ttrpg
Created: 4/20/2026
Status: 🔄 Open

Base: masterHead: fix/crash-on-eacces


📝 Commits (1)

  • 700d1e8 fix for unhandled error on eacces

📊 Changes

1 file changed (+18 additions, -10 deletions)

View changed files

📝 server/controllers/MiscController.js (+18 -10)

📄 Description

Brief summary

Prevent the server from crashing when POST /api/upload fails to create the destination directory, and surface a useful error message to the client instead of silently returning 200 on file-move failures.

Which issue is fixed?

TBD

In-depth Description

MiscController.handleUpload calls await fs.ensureDir(outputDirectory) with no error handling. Any file system error (EACCES, ESTALE, EIO, EROFS, ENOENT, …) causes an unhandled promise rejection. Because Express 4 does not forward async rejections to error-handling middleware, the rejection reaches the process-level unhandledRejection handler in Server.js, which calls process.exit(1) — taking the whole server down.

This is reproducible on an NFS-backed library folder with root_squash enabled: when ABS runs as uid 0 and tries to mkdir inside an author directory owned by a different uid, the server returns EACCES and the process exits.

There is a related lesser bug in the same handler: the per-file file.mv(...) loop logs failures but still returns 200 unconditionally, so partial or total upload failures are invisible to the client.

This PR:

  1. Wraps the ensureDir call in try/catch. On failure, logs with full context and returns 500 with the underlying error message (e.g. Failed to create upload directory: EACCES: permission denied, mkdir '/library/Author Name'). The existing client toast in client/pages/upload/index.vue already displays error.response?.data directly, so no client changes are needed.
  2. Collects per-file move failures. If any files fail, returns 500 with a summary of which files failed (and how many succeeded) so the user can retry only what is missing.

No behaviour change on the success path.

How have you tested this?

  1. Directory-create failurechmod 555 the parent library folder, attempt an upload from the web UI:
    • Before: server process exits with an unhandled rejection logged; the client connection drops.
    • After: server stays up; client shows a toast Failed to create upload directory: EACCES: permission denied, mkdir '…'.
  2. File-move failure — Uploaded files where the destination is writeable but file.mv fails (simulated by filling the target file system):
    • Before: client receives 200 and silently thinks everything uploaded.
    • After: client receives 500 with a message like Uploaded 2 of 3 files. Failed: badfile.m4b (ENOSPC: no space left on device, rename ...).
  3. Success path — Normal upload into a writeable library folder still returns 200, all files moved as before.
  4. Process stability — Verified the server process remains running after each failure case by monitoring ps and the log for the [Server] Unhandled rejection fatal line (which no longer appears).

Screenshots


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/advplyr/audiobookshelf/pull/5203 **Author:** [@rsJames-ttrpg](https://github.com/rsJames-ttrpg) **Created:** 4/20/2026 **Status:** 🔄 Open **Base:** `master` ← **Head:** `fix/crash-on-eacces` --- ### 📝 Commits (1) - [`700d1e8`](https://github.com/advplyr/audiobookshelf/commit/700d1e84599fee0de63bfc43700fab57f7943dc5) fix for unhandled error on eacces ### 📊 Changes **1 file changed** (+18 additions, -10 deletions) <details> <summary>View changed files</summary> 📝 `server/controllers/MiscController.js` (+18 -10) </details> ### 📄 Description ## Brief summary Prevent the server from crashing when `POST /api/upload` fails to create the destination directory, and surface a useful error message to the client instead of silently returning 200 on file-move failures. ## Which issue is fixed? TBD ## In-depth Description `MiscController.handleUpload` calls `await fs.ensureDir(outputDirectory)` with no error handling. Any file system error (`EACCES`, `ESTALE`, `EIO`, `EROFS`, `ENOENT`, …) causes an unhandled promise rejection. Because Express 4 does not forward async rejections to error-handling middleware, the rejection reaches the process-level `unhandledRejection` handler in `Server.js`, which calls `process.exit(1)` — taking the whole server down. This is reproducible on an NFS-backed library folder with `root_squash` enabled: when ABS runs as `uid 0` and tries to `mkdir` inside an author directory owned by a different uid, the server returns `EACCES` and the process exits. There is a related lesser bug in the same handler: the per-file `file.mv(...)` loop logs failures but still returns `200` unconditionally, so partial or total upload failures are invisible to the client. This PR: 1. Wraps the `ensureDir` call in `try/catch`. On failure, logs with full context and returns `500` with the underlying error message (e.g. `Failed to create upload directory: EACCES: permission denied, mkdir '/library/Author Name'`). The existing client toast in `client/pages/upload/index.vue` already displays `error.response?.data` directly, so no client changes are needed. 2. Collects per-file move failures. If any files fail, returns `500` with a summary of which files failed (and how many succeeded) so the user can retry only what is missing. No behaviour change on the success path. ## How have you tested this? 1. **Directory-create failure** — `chmod 555` the parent library folder, attempt an upload from the web UI: - Before: server process exits with an unhandled rejection logged; the client connection drops. - After: server stays up; client shows a toast `Failed to create upload directory: EACCES: permission denied, mkdir '…'`. 2. **File-move failure** — Uploaded files where the destination is writeable but `file.mv` fails (simulated by filling the target file system): - Before: client receives `200` and silently thinks everything uploaded. - After: client receives `500` with a message like `Uploaded 2 of 3 files. Failed: badfile.m4b (ENOSPC: no space left on device, rename ...)`. 3. **Success path** — Normal upload into a writeable library folder still returns `200`, all files moved as before. 4. **Process stability** — Verified the server process remains running after each failure case by monitoring `ps` and the log for the `[Server] Unhandled rejection` fatal line (which no longer appears). ## Screenshots --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
adam added the pull-request label 2026-04-25 00:51:04 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/audiobookshelf#4461