actualbudget/actual · critical

Error inserting: table "${table}" does not exist

Error message

Error inserting: table "${table}" does not exist

What it means

convertForInsert verifies the target table exists in the schema before iterating its fields to apply defaults. An unknown table during insert throws 'Error inserting: table "X" does not exist' — the insert-specific variant of the conform table check.

Source

Thrown at packages/loot-core/src/server/aql/schema-helpers.ts:156

        }

        // This option removes null values (see `convertForInsert`)
        if (skipNull && obj[field] == null) {
          return null;
        }

        return [fieldRef(field), convertInputType(obj[field], fieldDesc.type)];
      })
      .filter(Boolean),
  );
}

export function convertForInsert(schema, schemaConfig, table, rawObj) {
  const obj = { ...rawObj };

  const tableSchema = schema[table];
  if (tableSchema == null) {
    throw new Error(`Error inserting: table "${table}" does not exist`);
  }

  // Inserting checks all the fields in the table and adds any default
  // values necessary
  Object.keys(tableSchema).forEach(field => {
    const fieldDesc = tableSchema[field];

    if (obj[field] == null) {
      if (fieldDesc.default !== undefined) {
        obj[field] =
          typeof fieldDesc.default === 'function'
            ? fieldDesc.default()
            : fieldDesc.default;
      } else if (isRequired(field, fieldDesc)) {
        // Although this check is also done in `conform`, it only
        // checks the fields in `obj`. For insert, we need to do it
        // here to check that all required fields in the table exist
        throw new Error(

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Correct the table name to a valid schema table ('transactions', 'accounts', etc.).
  2. Grep the aql schema file for the exact registered table names.
  3. If the table is new, add its schema definition before inserting.
  4. Ensure the `schema` object passed into convertForInsert is the current full schema map, not a partial/stale copy.

Example fix

// before
insertWithSchema('account', obj, {}); // wrong table
// after
insertWithSchema('accounts', obj, {});
Defensive patterns

Strategy: validation

Validate before calling

const VALID_INSERT_TABLES = new Set(Object.keys(schema));
function safeInsert(table, obj) {
  if (!VALID_INSERT_TABLES.has(table)) throw new Error(`Unknown insert table: ${table}`);
  return insertWithSchema(table, obj, {});
}

Try / catch

try {
  return insertWithSchema(table, obj, {});
} catch (e) {
  if (e.message.includes('Error inserting: table')) {
    logger.error('Insert against unknown table', { table });
    return null;
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling convertForInsert (directly or via insertWithSchema / toDb inside a transactor) with a table name that is misspelled, unregistered, or removed in the current schema version.

Common situations: Plugins/custom sync code written against old internal table names, copy-pasted insert code with a wrong table constant, and migrations that dropped or renamed a table without updating insert call sites.

Related errors


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