actualbudget/actual · error
File not found
Error message
File not found
What it means
updateFileOwner runs an UPDATE on files and checks result.changes; if the UPDATE matched zero rows it throws 'File not found', meaning no file with the given id exists. This distinguishes a no-op update from a successful ownership change.
Source
Thrown at packages/sync-server/src/services/user-service.ts:139
oldUserId,
]);
});
} catch (error) {
throw new Error(`Failed to transfer files: ${error.message}`);
}
}
export function updateFileOwner(ownerId, fileId) {
if (!ownerId || !fileId) {
throw new Error('Invalid parameters');
}
try {
const result = getAccountDb().mutate(
'UPDATE files set owner = ? WHERE id = ?',
[ownerId, fileId],
);
if (result.changes === 0) {
throw new Error('File not found');
}
} catch (error) {
throw new Error(`Failed to update file owner: ${error.message}`);
}
}
export function getUserAccess(fileId, userId, isAdmin) {
return getAccountDb().all(
`SELECT users.id as userId, user_name as userName, files.owner, display_name as displayName
FROM users
JOIN user_access ON user_access.user_id = users.id
JOIN files ON files.id = user_access.file_id
WHERE files.id = ? and (files.owner = ? OR 1 = ?)`,
[fileId, userId, isAdmin ? 1 : 0],
);
}
export function countUserAccess(fileId, userId) {View on GitHub (pinned to d4334cb6e6)
Solutions
- Confirm the fileId exists: getFileById(fileId) should return a row before updating
- Re-fetch the current file list from the server; if the file was deleted, remove the stale reference on the client
- Verify you are querying the same account database the file was created in
- Check for id mismatch (cloud file id vs local id) and use the id stored in the files table
Example fix
// before
await updateFileOwner(newOwnerId, fileId); // throws if deleted
// after
if (!getFileById(fileId)) {
logger.warn(`File ${fileId} no longer exists; skipping owner update`);
return;
}
await updateFileOwner(newOwnerId, fileId); Defensive patterns
Strategy: validation
Validate before calling
import { getFileById } from './services/user-service';
if (!getFileById(fileId)) {
throw new Error(`File ${fileId} does not exist; refresh file list`);
} Type guard
function fileExists(fileId) {
return typeof fileId === 'string' && fileId.length > 0 && getFileById(fileId) !== null;
} Try / catch
try {
updateFileOwner(ownerId, fileId);
} catch (e) {
if (e.message.includes('File not found')) {
logger.warn(`Skipping owner update for missing file ${fileId}`);
return;
}
throw e;
} Prevention
- Re-fetch the file list from the server instead of trusting cached ids
- Handle deleted files gracefully in admin UIs (skip, not crash)
- Distinguish cloud file ids from local budget ids
- Verify all instances point at the same account database
When it happens
Trigger: Calling updateFileOwner with a fileId that does not exist in the files table — deleted file, id from a different budget/database, typo'd or truncated id, or fileId pointing to a file the requesting server instance never synced.
Common situations: Client holds a stale file id after the file was deleted server-side; restoring a database from backup so newer file ids are gone; multi-instance setups where files live in a different account.sqlite; passing the internal cloud file id vs the local budget id interchangeably.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- File not found
- File does not exist or you don't have access to it
- Could not update File
- Invalid file ID
- Invalid group ID
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/28fc064bb179b76c.
Report an issue: GitHub.