actualbudget/actual · error
error
error
Error message
error
What it means
The POST /update-user-file endpoint failed while writing the uploaded budget file to disk (fs.writeFile inside a try/catch). The server logs 'Error writing file' with the underlying error and responds 500 with { status: 'error' } instead of storing the file and creating a new group id. This is a server-side filesystem failure, not a client payload problem.
Source
Thrown at packages/sync-server/src/app-sync.ts:366
? requireFileAccess(currentFile, res.locals.user_id)
: null;
if (fileAccessError) {
res.status(403);
res.send(fileAccessError);
return;
}
const errorMessage = validateUploadedFile(groupId, keyId, currentFile);
if (errorMessage) {
res.status(400).send(errorMessage);
return;
}
try {
await fs.writeFile(getPathForUserFile(fileId), req.body);
} catch (err) {
console.log('Error writing file', err);
res.status(500).send({ status: 'error' });
return;
}
if (!currentFile) {
// it's new
const newGroupId = generateGroupId();
groupId = newGroupId;
filesService.set(
new File({
id: fileId,
groupId: newGroupId,
syncVersion: syncFormatVersion,
name,
encryptMeta,
owner:
res.locals.user_id ||
(() => {
throw new Error('User ID is required for file creation');View on GitHub (pinned to d4334cb6e6)
Solutions
- Verify the directory configured in userFiles exists and is writable by the sync-server process; create it and fix ownership/permissions (mkdir -p, chown).
- Check free disk space on the volume backing userFiles (df -h) and free space if full.
- Re-run the sync from the client after fixing storage; the client will re-upload the file.
- Inspect server logs for the 'Error writing file' entry to see the exact errno (ENOENT/EACCES/ENOSPC) and address that cause.
Example fix
// before (host): userFiles points at a path not created in the container // docker run -v data:/data actual-sync # but USER_FILES=/app/user-files missing // after // docker run -v data:/app/user-files -e ACTUAL_USER_FILES=/app/user-files actual-sync // or create the dir before starting: mkdir -p "$USER_FILES"
Defensive patterns
Strategy: retry
Validate before calling
// before upload, ensure storage exists
import fs from 'fs';
if (!fs.existsSync(userFilesDir) || !fs.statSync(userFilesDir).isDirectory()) {
throw new Error('userFiles directory missing');
} Try / catch
const res = await uploadFile(fileId, body);
if (res.status === 500) {
// server-side write failure: fix storage, then retry
await ensureStorageAvailable();
await uploadFile(fileId, body);
} Prevention
- Provision and chmod/chown the userFiles directory before starting the server.
- Monitor disk space on the volume backing userFiles.
- In containers, mount a persistent volume at the exact path userFiles points to.
- Alert on the server-side 'Error writing file' log line.
When it happens
Trigger: Uploading a user file via POST /update-user-file when fs.writeFile(getPathForUserFile(fileId), req.body) throws — e.g. the userFiles directory is missing, disk is full, or the process lacks write permission to the directory.
Common situations: Docker volume not mounted or mounted read-only so the userFiles dir doesn't exist; full disk on self-hosted instances; running the sync server as a user without permissions on the data directory; ENOSPC/ENOENT/EACCES errors.
Understand the failure class
Background: Error: deliberate library guards and refused operations — this error's family across 54 libraries.
Related errors
- Document directory is not set
- getDocumentDir: id is falsy: ${id}
- Failed to duplicate budget file: ${error.message}
- Invalid budget id "${id}". Check the id of your budget in th
- File not found at the provided path: ${filepath}
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/f8bcf7a3a9e65913.
Report an issue: GitHub.