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
- Use exactly one identifier per nested array entry: [Storage, [Logger]] resolves Storage via get and Logger[] via getAll
- Give each dependency its own top-level entry instead of grouping several inside one nested array
- 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
- Remember the convention: bare identifier = get, [identifier] (exactly one) = getAll
- Never group multiple identifiers inside one nested array
- Validate dynamically built deps arrays before passing them to service()/impl()
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)