actualbudget/actual · error · TypeError

UUID format no longer matches expected format

Error message

UUID format no longer matches expected format

What it means

generateGroupId creates a sync GroupId via uuidv4() and then re-validates it with isValidGroupId (a guard against the UUID format changing across library upgrades). If the generated UUID no longer matches the expected format, it throws a TypeError 'UUID format no longer matches expected format'. This is a defensive invariant check, essentially unreachable in normal operation.

Source

Thrown at packages/sync-server/src/app-sync.ts:73

  express.raw({
    type: 'application/encrypted-file',
    limit: `${config.get('upload.syncEncryptedFileSizeLimitMB')}mb`,
  }),
);
app.use(express.json({ limit: `${config.get('upload.fileSizeLimitMB')}mb` }));

export { app as handlers };

const OK_RESPONSE = { status: 'ok' };

function boolToInt(deleted: boolean) {
  return deleted ? 1 : 0;
}

function generateGroupId(): GroupId {
  const id = uuidv4();
  if (!isValidGroupId(id)) {
    throw new TypeError('UUID format no longer matches expected format');
  }
  return id;
}

function extractSingleHeader(
  req: Request,
  res: Response,
  key: string,
): string | null {
  const value = req.headers[key];
  if (!value) {
    return null;
  }
  if (typeof value !== 'string') {
    res.status(400).send('Duplicate headers encountered for key ' + key);
    return null;
  }
  return value;

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Check the installed uuid package version/implementation and restore standard v4 output (lowercase 8-4-4-4-12).
  2. If uuidv4 is mocked in tests, make the mock return a valid v4 UUID string.
  3. Inspect isValidGroupId and update it only if the ID format intentionally changed across the codebase.
  4. Reinstall dependencies (yarn install) to rule out a corrupted/pinned wrong uuid version.

Example fix

// before
vi.mock('uuid', () => ({ v4: () => 'test-id' }));

// after
vi.mock('uuid', () => ({ v4: () => '0b9e6b1e-7a4c-4f3e-9d2a-1c8b5f6e7a90' }));
Defensive patterns

Strategy: type-guard

Validate before calling

function isValidGroupId(id: string): boolean {
  return /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(id);
}
const id = uuidv4();
if (!isValidGroupId(id)) throw new TypeError('uuid source produced invalid v4 id');

Type guard

function isUuidV4(value: string): value is `${string}-${string}-${string}-${string}-${string}` {
  return /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(value);
}

Prevention

When it happens

Trigger: Only when uuidv4() returns a value that fails isValidGroupId — practically meaning the uuid library was swapped/upgraded to produce a different format (e.g. non-lowercase or braced UUIDs) or mocked to return non-UUID values.

Common situations: Dependency upgrade or replacement of the uuid package; test mocks stubbing uuidv4 with values like 'mock-id'; custom crypto.randomUUID polyfills returning a different casing/format.

Related errors


AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29). Data as JSON: /api/errors/7f8c24a013979199. Report an issue: GitHub.