actualbudget/actual · error
invalid-file-id
invalid-file-id
Error message
invalid fileId
What it means
The Pluggy.ai integration's POST /status endpoint validates the X-Actual-File-Id header with isValidFileId when present. If the header value is not a valid file id, it returns HTTP 400 with reason 'invalid-file-id' and details 'invalid fileId'. This is an input-validation guard before any authorization or database lookup.
Source
Thrown at packages/sync-server/src/app-pluggyai/app-pluggyai.js:31
import { pluggyaiService } from './pluggyai-service';
const app = express();
export { app as handlers };
app.use(requestLoggerMiddleware);
app.use(express.json());
app.use(validateSessionMiddleware);
function canAccessFile(fileId, userId) {
return isAdmin(userId) || UserService.countUserAccess(fileId, userId) > 0;
}
app.post(
'/status',
handleError(async (req, res) => {
const fileId = req.get('X-Actual-File-Id');
if (!!fileId) {
if (!isValidFileId(fileId)) {
res.status(400).send({
status: 'error',
reason: 'invalid-file-id',
details: 'invalid fileId',
});
return;
}
if (!canAccessFile(fileId, res.locals.user_id)) {
res.status(403).send({
status: 'error',
reason: 'file-access-denied',
details: "You don't have permissions over this file",
});
return;
}
}
const source = pluggyaiService.getCredentialSource(fileId);View on GitHub (pinned to d4334cb6e6)
Solutions
- Send the correct budget fileId — the exact id string from the Actual app/server — in X-Actual-File-Id
- Omit the X-Actual-File-Id header entirely if no budget context is needed (the check only runs when a value is present)
- Log the outgoing header and compare it against a fileId listed by the sync-server
- Regenerate/locate the id via the server API rather than manual input
Example fix
// before
headers: { 'X-Actual-File-Id': 'My Budget' }
// after
headers: { 'X-Actual-File-Id': '3f1c9a2e-...-valid-uuid' } Defensive patterns
Strategy: validation
Validate before calling
const fileId = '3f1c9a2e-8b7d-4c2a-9e1f-0d6b5a4c3e2d'; // example
function isPlausibleFileId(id) {
return typeof id === 'string' && id.trim().length > 0 && /^[0-9a-f-]{16,}$/i.test(id.trim());
}
if (!isPlausibleFileId(fileId)) throw new Error('Refusing request: malformed fileId'); Type guard
function isValidFileId(v) {
return typeof v === 'string' && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(v);
} Try / catch
const res = await fetch('/pluggyai/status', { headers: { 'X-Actual-File-Id': fileId } });
if (res.status === 400) {
const body = await res.json();
if (body.reason === 'invalid-file-id') {
throw new Error(`Bad fileId header: ${body.details}`);
}
} Prevention
- Copy fileIds from the app/server listing, never type them manually
- Omit the header entirely when no budget is targeted
- Keep a constants file of validated budget ids
- Log and diff outgoing headers in integration tests
When it happens
Trigger: POST /status with X-Actual-File-Id set to a malformed/non-UUID value (empty-ish string, arbitrary text, truncated id).
Common situations: Client integration hard-coding a wrong id; passing a budget name instead of its fileId; copy/paste losing characters; sending the header with an empty-but-present value.
Related errors
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/730a108581efe653.
Report an issue: GitHub.