actualbudget/actual · error
User ID is required for file creation
Error message
User ID is required for file creation
What it means
Thrown inline in the file-creation handler (POST /files) of app-sync.ts when the authenticated request has no user_id on res.locals. The sync server requires every uploaded file to be owned by a known user, so if the auth middleware did not populate user_id, creation is aborted with this error instead of writing an orphaned file row.
Source
Thrown at packages/sync-server/src/app-sync.ts:384
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');
})(),
}),
);
res.send({ status: 'ok', groupId });
return;
}
if (!groupId) {
// sync state was reset, create new group
const newGroupId = generateGroupId();
groupId = newGroupId;
filesService.update(fileId, new FileUpdate({ groupId: newGroupId }));
}
// Regardless, update some properties
filesService.update(
fileId,View on GitHub (pinned to d4334cb6e6)
Solutions
- Ensure the request carries a valid access token so the auth middleware sets res.locals.user_id.
- Verify the auth middleware is registered before the file-creation route and matches the method (header vs openid).
- If behind a proxy, confirm it forwards the Authorization header to the sync server.
- Check server logs/auth config (ENABLE_OPENID, trusted proxies) to see why authentication was skipped.
Example fix
// before
curl -X POST http://server/files -d '{...}' // no token -> 500 'User ID is required...'
// after
curl -X POST http://server/files -H "Authorization: Bearer <token>" -d '{...}' Defensive patterns
Strategy: validation
Validate before calling
// client-side, before POST /files
if (!accessToken) throw new Error('Not authenticated: no access token for file upload');
const res = await fetch(base + '/files', { headers: { Authorization: `Bearer ${accessToken}` }, ... });
if (res.status === 401 || res.status === 403) throw new Error('Auth middleware did not attach user identity'); Type guard
function hasUserId(locals: unknown): locals is { user_id: string } {
return typeof locals === 'object' && locals !== null && typeof (locals as any).user_id === 'string' && (locals as any).user_id.length > 0;
} Try / catch
try {
await createFile(payload);
} catch (err) {
if (err instanceof Error && err.message.includes('User ID is required')) {
await reauthenticate();
return createFile(payload);
}
throw err;
} Prevention
- Always send a valid Authorization header on sync-server API calls.
- Register auth middleware before all app-sync routes and add integration tests for unauthenticated requests.
- Check proxy configs strip-headers settings for Authorization.
- After upgrading the server, re-verify the auth flow end to end.
When it happens
Trigger: POST to the file-upload endpoint while res.locals.user_id is empty — i.e. the request reached the handler without going through (or failing silently in) the auth middleware, or the token validation step did not attach the user identity.
Common situations: Deployments where the auth middleware is misconfigured or omitted from the route stack; self-hosted setups with a reverse proxy that strips the Authorization header; custom clients calling the endpoint without a valid token; version mismatches after upgrading the sync server.
Related errors
- login: User token not set
- getServerErrorReason(json)
- File does not exist or you don't have access to it
- token-not-found
- token-expired
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/5a5ca3ea0bf754df.
Report an issue: GitHub.