cocoindex-io/cocoindex · error · ValueError

Unsupported LanceDB column action for in-place evolution: {a

Error message

Unsupported LanceDB column action for in-place evolution: {action!r}

What it means

During LanceDB target schema reconciliation, _apply_column_actions only supports additive in-place schema evolution ('insert'/'upsert' actions, which add new nullable columns backfilled with null). Any other DiffAction (e.g. delete, update) for a column subkey means the desired change cannot be applied in place, so the library raises rather than corrupting the table. Dropping or altering columns requires recreating the table instead.

Source

Thrown at python/cocoindex/connectors/lancedb/_target.py:1160

                continue
            if col_name in existing_cols:
                continue

            desired_col = schema.columns.get(col_name)
            if desired_col is None:
                continue

            if action in ("insert", "upsert"):
                fields_to_add.append(
                    # Existing rows are backfilled with null, so additive schema
                    # evolution must materialize the new column as nullable.
                    pa.field(col_name, desired_col.type, nullable=True)
                )
                if desired_col.nullable:
                    null_backfilled_columns.add(col_name)
                continue

            raise ValueError(
                f"Unsupported LanceDB column action for in-place evolution: {action!r}"
            )

        if fields_to_add:
            await table.add_columns(fields_to_add)
            return frozenset(null_backfilled_columns)
        return frozenset()

    def reconcile(
        self,
        key: coco.StableKey,
        desired_state: _TableSpec | coco.NonExistenceType,
        prev_possible_records: Collection[_TableTrackingRecord],
        prev_may_be_missing: bool,
        /,
    ) -> (
        coco.TargetReconcileOutput[_TableAction, _TableTrackingRecord, _RowHandler]
        | None

View on GitHub (pinned to e84aa99b32)

Solutions

  1. Drop the existing LanceDB table (or point the target at a new table name) so it is recreated from the current declared schema
  2. Restore the removed/changed column in the declared schema so the diff is only additive
  3. Do a two-phase migration: add the new column (supported additive op), backfill, then manually remove the old column via LanceDB tooling
  4. Inspect the diff actions by comparing schema.columns with the live table schema to identify the offending column

Example fix

// before
class Row(NamedTuple):
    id: str
    text: str
    legacy_col: str  # removed later -> 'delete' action -> error
// after
class Row(NamedTuple):
    id: str
    text: str
# and recreate the table (or use a fresh table name) since column drops are not in-place
Defensive patterns

Strategy: validation

Validate before calling

live_cols = set((await table.schema()).names)
declared = set(schema.columns)
if not (declared >= live_cols and live_cols <= declared | set()):
    # non-additive diff possible: drops or type changes
    if live_cols - declared:
        raise RuntimeError(f"columns removed; recreate table: {live_cols - declared}")

Type guard

def is_additive(actions: dict[str, str]) -> bool:
    return all(a in ("insert", "upsert") for a in actions.values())

Try / catch

try:
    await app.update()
except ValueError as e:
    if "Unsupported LanceDB column action" in str(e):
        logger.warning("non-additive schema change; recreating table: %s", e)
        await drop_lancedb_table(table_name)
        await app.update()
    else:
        raise

Prevention

When it happens

Trigger: A diff between the declared table schema and the existing LanceDB table produces a non-additive column action (delete/update/replace) in column_actions, e.g. a column was removed or its type changed in the declared TableSpec while the table already exists.

Common situations: Removing or renaming a field in a row dataclass after a table already has data; changing a column's type; changing a column to/from primary key in ways the additive path skips; running an updated pipeline version against an old LanceDB table.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08). Data as JSON: /api/errors/0b5a022ca1199c8e. Report an issue: GitHub.