toeverything/AFFiNE · error

Invalid rowId

Error message

Invalid rowId

What it means

When a component is registered with a dependency array (framework.service/impl/override with [deps]), dependenciesToFactory interprets each entry as exactly one constructor argument: a bare identifier resolves via provider.get, and a nested array resolves via provider.getAll (all variants) but must contain exactly one identifier. A nested array of any other length matches neither form, so the factory throws 'Invalid dependency' the first time the component is constructed.

Solutions

  1. Use exactly one identifier per nested array entry: [Storage, [Logger]] resolves Storage via get and Logger[] via getAll
  2. Give each dependency its own top-level entry instead of grouping several inside one nested array
  3. If deps are computed at runtime, assert each entry is an Identifier or a length-1 array before calling service()/impl()

Example fix

// before
fw.service(A, [Storage, [Cache, Logger]]); // nested array length 2 -> 'Invalid dependency'

// after
fw.service(A, [Storage, [Cache]]); // getAll(Cache)
// A's constructor receives (storage: Storage, caches: Cache[], provider)
Defensive patterns

Strategy: type-guard

Validate before calling

function assertValidDeps(deps: unknown[]) {
  for (const dep of deps) {
    if (Array.isArray(dep) && dep.length !== 1) {
      throw new Error(`Invalid dependency: nested arrays must hold exactly one identifier, got ${dep.length}`);
    }
  }
}
assertValidDeps([Storage, [Cache]]); // ok
// assertValidDeps([Storage, [Cache, Logger]]); // fails fast with your own message

Type guard

const isValidDep = (dep: unknown) =>
  !Array.isArray(dep) || dep.length === 1;

Prevention

When it happens

Trigger: Declaring deps as [Storage, [Cache, Logger]] intending two same-kind dependencies; passing an empty nested array [[]] where [[Identifier]] was meant; dynamically building deps arrays and accidentally nesting several identifiers into one sub-array.

Common situations: Constructor signatures refactored so several deps arrive together; code that spreads a list into one nested array ([...ids] instead of ids.map(id => [id])); teams assuming the nested array means 'array of these deps, plural' when it strictly means getAll of ONE identifier.

Related errors


AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18). Data as JSON: /api/errors/cf2e2a8eed42e663. Report an issue: GitHub.

Appendix: source

Thrown at blocksuite/affine/blocks/database/src/utils/block-utils.ts:190

        value: cell.value,
      };
    }
  });
}

export function updateCells(
  model: DatabaseBlockModel,
  columnId: string,
  cells: Record<string, unknown>
) {
  model.store.transact(() => {
    Object.entries(cells).forEach(([rowId, value]) => {
      if (
        rowId === '__proto__' ||
        rowId === 'constructor' ||
        rowId === 'prototype'
      ) {
        throw new Error('Invalid rowId');
      }
      if (!model.props.cells[rowId]) {
        model.props.cells[rowId] = Object.create(null);
      }
      if (model.props.cells[rowId]) {
        model.props.cells[rowId][columnId] = {
          columnId,
          value,
        };
      }
    });
  });
}

export function updateProperty(
  model: DatabaseBlockModel,
  id: string,
  updater: ColumnUpdater,

View on GitHub (pinned to b4c8548c09)