Lattice Grid Buy a licence

diagnostics

JavaScript Data Grid Warnings Reference

A grid prints one line to the console for a mistake it can name on the spot rather than fail on silently. Every one of them is catalogued here: 747 messages across the grid and every module, what each means, and the specific change that clears it.

The grid (core) (283)

  • *.unknown:* '{key}' is not a configuration key this grid recognises, so it had no effect on {column|column group} "{name}". Check the spelling against the reference.
  • apply:* column "{colId}" has no `field` and no `value.apply`, so an edit cannot be written back.
  • bus.beforeReject:* a '{type}' before-handler rejected; the action was cancelled.
  • bus.beforeThrow:*:* a '{type}' before-handler threw; the action was cancelled.
  • bus.handler:* {on|once}('{type}') needs a function; ignored.
  • bus.throw:*:* a '{type}' handler threw; other handlers were unaffected.
  • bus.unknown:* {on|once}('{type}') subscribes to an event the grid never raises, so the handler will never fire. Check the name against the EventName list - a common cause is a near miss such as 'row:click' for 'row:clicked'.
  • changes.unkeyed rows.apply needs a rowKey to identify rows; without one, updates and removals cannot be matched.
  • clipboard/no-read the clipboard could not be read; handle the paste event and pass event.clipboardData instead
  • column.cell.autoHeight `cell.autoHeight` is not a column option, so it had no effect on column "{column}". Rows grow to fit with the grid-level `autoHeight`; `cell.wrap` lets this column's text wrap.
  • column.editor-not-enabled:* column '{id}' names the editor '{editor}' but does not enable editing, so it stays read-only. Add `enabled: true`, or use `edit: true` and set `editor` alongside it.
  • column.timeZone `timeZone` is not a column option, so it had no effect on column "{column}". A date column stores, displays, sorts and filters in the grid-level `timeZone` (else the viewer's); set `timeZone` on the grid, not the column.
  • columns.duplicate:* two columns resolve to the id '{id}', so the second is ignored. An id defaults to the column's field, so declare an explicit `id` when two columns read the same field.
  • columns.fit-overflow:* columns.fit(): the columns it does not size (resizable: false, and the grid's own checkbox, expander, group and tree columns) take {n}px of a {m}px viewport, leaving {k}px for {c} column(s) that need at least {f}px. They are set to their minimum widths and the grid scrolls
  • columns.group.dissolve:* no band "{id}" to dissolve.
  • columns.group.empty groupColumns was given no known column ids; nothing was grouped.
  • columns.group.idtaken:* band id "{id}" is already in use; nothing was grouped.
  • columns.group.locked:* column "{id}" is position-locked and cannot be grouped.
  • columns.group.move:* no band "{id}" to move.
  • columns.group.rename:* no band "{id}" to rename.
  • columns.group.unknown:* no band "{id}" to add to; nothing was grouped.
  • columns.ungroup.unknown:* no column "{id}" to ungroup.
  • comments.provider comments provider is missing {methods}; those operations are unavailable.
  • compareGroups:by compareGroups needs a "by" column to split the rows into two groups.
  • compareGroups:degenerate compareGroups: the chosen test had too little to work with (too few values, or no variation).
  • compareGroups:distinct compareGroups: the two groups must be two distinct, present values.
  • compareGroups:empty compareGroups: one of the two groups has no rows in the current set.
  • compareGroups:groups compareGroups: the "by" column has fewer than two distinct values to group by.
  • compareGroups:numeric-override compareGroups: {test} needs a numeric column, but the column is not numeric.
  • config.rows.duplicated rows were declared twice - as the top-level `rows` option and inside the `source` block - so the top-level rows were used and the {n} row(s) in `source.rows` were ignored. Declare them once.
  • config.unknown:* '{key}' is not a configuration key this grid recognises, so it had no effect. Check the spelling against the reference.
  • config.value:* '{key}' was given {value}, which is not one of the shapes it accepts. Check the type against the reference.
  • count:* column "{id}" is bitset-backed with no declared row count; assuming {n}
  • date.pattern.token:* the date pattern "{pattern}" contains '{ch}', which is not a supported token, so it is rendered as text. Quote it as a literal to silence this. Supported: {tokens}.
  • defaults:cyclic defaults() expects a configuration object with no circular references, but the object passed refers back to itself. The call was ignored and the defaults are unchanged.
  • defaults:type defaults() expects a configuration object, or null to clear it, but got {type}. The call was ignored and the defaults are unchanged.
  • deps:missing:* computed column "{colId}" declares no `value.deps`; treating it as `deps: '*'` (a whole-row dependency). Declare the dependencies to get precise invalidation.
  • detail.rows detail is enabled but neither `rows` nor `render` was supplied; expanding a master would show an empty region, so it stays off.
  • diff:compute-threw:* column '{colId}' compute threw while diffing; the column was left out of the row's diff instead of being counted as a change.
  • diff.compare.throw:* the comparator for column '{colId}' threw while diffing; the two values were compared by identity instead.
  • diff.keyOf.throw the diff `keyOf` threw; the row was skipped.
  • diff.norows the diff was asked about the whole data set but has no current rows: pass them in, or supply `currentRows` when constructing the DiffModel.
  • diff.override.throw:* the diff comparator override threw; the column comparator was used instead.
  • diff.read.throw:* reading column '{colId}' threw while diffing.
  • diff.unkeyed {n} snapshot row(s) produced no key and were dropped from the diff; `keyOf` must be the grid's own `rowKey`.
  • edit.apply.throw:* value.apply threw for column '{colId}'; the edit was discarded.
  • edit.confirm.nocommit edit.confirm "manual" needs an edit.commit hook to send the write; optimistic tracking is off.
  • edit.confirm.unknown edit.confirm '{value}' is not a mode; use 'auto' or 'manual'. Treated as 'auto'.
  • edit.enabled.throw:* edit.enabled threw for column '{colId}'; the cell was treated as read-only.
  • edit.getValue.throw:* getValue threw for column '{colId}'.
  • edit.nofield:* column '{colId}' is editable but has neither a field nor value.apply, so the edit had nowhere to go.
  • edit.parse.throw:* value.parse threw for column '{colId}'; the raw editor output was used.
  • edit.true.nocommit edit: true was set over a remote source but nothing can persist the write: no edit.commit hook and the source synthesised none (a read-only adapter, or one that does not declare mutate.update). Cells would change on screen
  • edit.validate.throw:* edit.validate threw for column '{colId}'; the edit was rejected.
  • explode:* column "{id}" has `group.explode`, so rows appear under each of their values and totals double-count. Group footers should be annotated accordingly.
  • export.excel.toExcelValue:* toExcelValue threw for column '{colId}'; the stored value was exported instead.
  • export/no-download download requested but this environment has no DOM; returning the data instead
  • export/no-worker no export worker supplied; streaming on the main thread instead
  • filter:col:* filter references unknown column "{col}"; the condition is refused rather than matching every row
  • filter:op:*:* filter operator "{op}" is unknown for column "{col}"; the condition is refused rather than matching every row. Valid operators here: {list}.
  • filter:operand:*:* filter condition on column "{col}" uses op "{op}" with an operand that cannot be read: {reason}. The condition is refused rather than installed.
  • filter:relative:*:* filter condition on column "{col}" uses op "relative" with {an unknown relative date token "…"|no relative date token}; the condition is refused rather than matching every row.
  • filters.reapply:* filters.reapply({name}) names no registered where predicate; nothing was re-run.
  • filters.where.condition:* filters.where("{name}", fn, { condition }) needs a FilterSet; the predicate is registered but nothing is pushed to the source.
  • filters.where.deps:* filters.where("{name}", fn, { deps }) needs an array of column ids; the predicate is registered but re-runs on every pass instead.
  • filters.where.fn:* filters.where("{name}", fn) needs a function; the predicate was not registered. Pass null to remove a registered predicate.
  • filters.where.name filters.where(name, fn) needs a non-empty string name; the predicate was not registered.
  • filters.where.threw:* the where predicate "{name}" threw; its rows are treated as not matching:
  • find:disabled find.open(): the find bar is off (config.find is false); grid.find(text) still searches, and find.matches() lists the results.
  • forecast.holtWinters.period forecast: holtWinters needs a period (opts.period >= 2)
  • format:* unrecognised format string "{spec}"; the value will be stringified
  • format:inapplicable:*:* column {col} has format key "{key}", which applies to a {other} column, not the {kind} column it was applied to; it is ignored.
  • format:pattern:* invalid number format pattern "{pattern}"; the value will use the default number format
  • format:shape format must be a spec object or a string; ignoring
  • format:unknown-style-key:*:* column {col} has format: { type: "{value}" }, which is not a valid format type ("number", "date", "boolean" or "text") and is ignored; did you mean style: "{value}" instead?
  • format:unknown:*:* column {col} has an unrecognised format key "{key}" for its {kind} format; it is ignored.
  • formatting.scale.mid.even a scale's "mid" needs an odd number of colours (a middle stop to pin); {n} were given, so "mid" was ignored.
  • formatting.scale.mid.range a scale's "mid" ({mid}) is outside its min..max range, so it was ignored.
  • grid.destroy teardown failed
  • grid.edit.addRow.unavailable edit.addRow was called but this source cannot append rows: it is not a remote source whose adapter declares mutate.append. The row was not added rather than shown on screen and never saved. Use a createPushdownSource over an adapter with mutate: { append: true }.
  • grid.edit.deleteRow.unavailable edit.deleteRow was called but this source cannot delete rows: it is not a remote source whose adapter declares mutate.delete. The row was not removed rather than hidden and then reappearing. Use a createPushdownSource over an adapter with mutate: { delete: true }.
  • grid.edit.deleteRows.disabled edit.deleteRows (and the Delete-key / "Delete row" gestures) were called but row deletion is off. Set config.rowDelete: true to enable it; deleting data on a keystroke is opt-in. Deletion still flows through beforeDelete so it can be confirmed or vetoed.
  • grid.rolling.orderBy:*:* a '{kind}' rolling column needs an 'orderBy' column to define its order - the screen sort is never used, because a rolling figure would then change on every header click. It reported null until an orderBy is set.
  • grid.rolling.window:*:* a '{kind}' rolling column needs a positive window span (a count of rows, or a time span in ms or 'minutes'); it had none, so the column reported null.
  • grid.sort.shape.* sort ignores an entry with keys { {keys} }; expected { col, dir } (the keys are "col" and "dir", not "colId"/"direction").
  • grid.sort.unknown.* sort ignores unknown column "{col}".
  • grid.structural.compositekey a structural append/delete needs a single-field rowKey so the optimistic row can be rekeyed to the server id; with a composite or function rowKey the temp key cannot be written back onto the row. Set rowKey to one field name to use add-row / delete-row.
  • grid.ts.multiplicative:* a multiplicative decomposition of '{col}' meets a non-positive value, where the ratio value ÷ trend is undefined; those rows report null. Use the additive model for a series that crosses or sits at zero.
  • grid.ts.period:*:* a '{kind}' decomposition column needs a declared integer 'period' of at least 2 (the season length - 7 for a weekly cycle in daily data, say); there is no auto-detection, so it reported null until a period is set.
  • groupDefaultExpanded.threw config.groupDefaultExpanded threw: {error}; the group starts expanded.
  • grouping.cardinality:* grouping by {cols} produced {g} groups for {n} rows: nearly one group per row. Grouping a continuous value such as a percentage or a timestamp rarely reads usefully; consider a computed banding column.
  • history.apply.throw applying the {direction} of '{label}' threw.
  • history.capture.throw history capture threw; the action was not recorded.
  • history.edit.throw edit {direction} threw; the entry was dropped.
  • i18n:locale:not-loaded:* locale "{locale}" is not loaded: import "@toclocoinc/lattice-grid/locales/{locale}" to add its text; English is used until then.
  • i18n:unknown:* message keys not in the catalogue and so ignored: {keys}
  • iconSet:* unknown icon set '{name}'; falling back to arrows.
  • import.apply.bytes grid.import.apply is synchronous and cannot inflate an .xlsx: await grid.import.xlsx(bytes) for the records (or grid.import.previewXlsx for a preview), then pass those to apply.
  • import.apply.input grid.import.apply was given something it cannot import: pass delimited text, a preview from grid.import.preview, or an array of record objects.
  • import.apply.source grid.import.apply changes the grid's own rows, which only a memory-source grid holds. On a remote, paged or stream source the rows come from the backend; import the file there, or load a memory grid from grid.import.csv(text).
  • import.xlsx.nosheet grid.import.xlsx found no worksheet in the .xlsx; nothing to import.
  • import.xlsx.sheet grid.import.xlsx has no worksheet "{sheet}"; the workbook has: {sheets}.
  • impure:*:* column "{colId}" is an impure computed column (`value.pure: false`) and is being used to {operation}. The result is not stable across passes.
  • infer:* column "{colId}" has mixed or ambiguous values and was inferred as "text". Declare `type` on the column to get correct sorting, filtering and export.
  • ingest.impure.* column "{id}" is an impure computed column and is not materialised into the store
  • ingest.mixed.* column "{id}" holds mixed or unrecognised value types; storing as an object array. Declare a type to avoid this.
  • licence.* no licence key supplied. The grid renders in full and carries a trial watermark on any host other than localhost. Get a key at latticegrid.dev.
  • licence.algorithm this host does not provide crypto.subtle Ed25519, so the licence signature could not be verified. A structural check was used instead.
  • licence.domain licence key is not valid for {host}; it covers {domains}. The grid will render with a watermark.
  • licence.expired licence expired on {date}. The grid will render with a watermark. Renew at latticegrid.dev.
  • licence.product licence key is for '{got}', not '{want}'. The grid will render with a watermark.
  • licence.signature licence signature is not valid. The grid will render with a watermark.
  • lockVisible:* column "{id}" is `layout.lockVisible` and cannot be hidden
  • lookup:load:* column "{colId}" failed to load lookup options
  • lookup:options:* column "{colId}" has an invalid `lookup.options`; expected an array, a function, or a router route
  • lookup:unknown:* column "{colId}" holds value {value} which is not in its lookup options. Showing the raw value; set `lookup.unknownLabel` or `lookup.allowCustom` to change that.
  • move:* column "{id}" is not movable; the move was ignored
  • move.married:* band "{id}" declares marryChildren, so its columns stay together; the move was ignored
  • op:* unknown filter operator "{op}"; the condition is refused rather than matching every row
  • pagination:source-unsupported pagination is not available on a "{mode}" source, which has no page model to window; the grid scrolls through every row instead and shows no pager.
  • pending.reconcile.throw writing a server-authoritative value back threw; the cell may still show the optimistic value.
  • pending.revert.throw rolling back a rejected write threw; the cell may still show the rejected value.
  • pending.rows.append.nokey an appended row was confirmed without a server key (the adapter's mutate returned no keys and returning is not 'key'/'row'), so the row keeps its client temp key. It is persisted but cannot be addressed by its server id. Declare returning: 'key' or 'row' on the adapter so appended rows can be rekeyed.
  • pending.rows.rekey.throw rekeying an appended row from its temp key to the server key threw; selection, expansion, focus or in-flight cell writes on the row may still point at the temp key.
  • pending.rows.revert.throw rolling back a rejected structural write threw; the grid may still show the optimistic append or the tombstoned row.
  • pending.rows.stale a structural {kind} has been pending for over {n}s ({key}). With edit.confirm "manual" the host must call settle(id, ok); with "auto" the mutate promise never settled.
  • pending.stale a write has been pending for over {n}s ({key}/{colId}). With edit.confirm "manual" the host must call grid.edit.settle(id, ok); with "auto" the commit promise never settled.
  • perm* the permissions callback threw for column "{colId}"; that column and any later failure were treated as "{closed}". Permissions fail closed, so a broken callback removes columns rather than exposing them. This is reported once.
  • permissions.level:* {where} is "{value}", which is not one of {levels}; the column was treated as "{closed}" (permissions fail closed)
  • permissions.shape config.permissions is a {type}, which is not a level, a map, a list, a callback or a policy object; every column was treated as "hidden" (permissions fail closed)
  • permissions.throw:* the permissions callback threw for column "{colId}"; the column was treated as "{closed}". Permissions fail closed, so a broken callback removes columns rather than exposing them.
  • pivot:handles pivot was given total column ids but no handle(colId) resolver, so no cell values were reduced. Pass values: [{ colId, handle, fn }] or opts.handle.
  • pivot.maxColumns:* pivot would generate {n} columns, above the limit of {max}. Pivot on a lower-cardinality column, or raise pivot.maxColumns.
  • pivotView.no-dimension pivotView is on but the grid has no pivot dimension, so there is no matrix to draw. Put a column on the pivot (its `pivot` setting, or `pivotBy` in the query) for the columns across the top, and group by a column for the rows down the side.
  • presence.subscribe presence provider has no subscribe(); peers will never appear.
  • presentation.spotlight-inactive setSpotlight was called while no presentation is running, so nothing is dimmed. Start the presentation first, and note that stepping to a view clears the spotlight, so arm it after the deck has started rather than before.
  • preset:* unknown column preset "{name}"; ignoring
  • print/too-many-rows print refused: {rows} rows exceeds the ceiling of {max}. Export to Excel or CSV instead
  • radix:base:* unsupported radix {value}; use 2, 8, 16, 'binary', 'octal' or 'hex'. Falling back to hex
  • radix:preset:* unknown radix preset "{name}"; known presets are {list}
  • radix:unsigned:width signed: false needs a bitWidth to render a two's-complement pattern; defaulting to 32
  • radix:width:* unsupported bitWidth {value}; use 8, 16, 32 or 53. Ignoring it
  • redecorate:* columns.decorate('{id}', …): no column with id '{id}'.
  • refreshLookup:* columns.refreshLookup('{id}'): column has no `lookup`.
  • regex:* filter operator "matches" received an invalid pattern: {pattern}
  • registry.kind:* unknown registry kind '{kind}'; expected one of {list}.
  • registry.module.install:* module '{name}' has no install() and was ignored.
  • registry.module.reconfigure:* module '{name}' was re-registered with different options; the first registration is kept and this call was ignored. Unregister it first if you mean to replace it.
  • registry.module.shape registerModules received something that is not a module; ignored.
  • registry.module.throw:* module '{name}' threw during install; it was not registered.
  • registry.module.uninstall:* module '{name}' threw during uninstall.
  • registry.name:* register('{kind}', ...) needs a name.
  • registry.onChange.throw a registry change handler threw.
  • registry.replace:*:* '{name}' was already registered as a {kind} and has been replaced.
  • resize:* column "{id}" is not resizable; the resize was ignored
  • rowHeight.throw rowHeight function threw; the default height was used.
  • rowKey.emptyValue:* rowKey {named} is missing on {n} of {m} rows; the first such row has: {fields}. Rows without a key value collapse onto one key, so selection, edits and rows.apply updates land on whichever row now shares it. Set rowKey to a field present on every row, or fix the source data.
  • rowKey.missing no rowKey configured, so rows are identified by object identity. This disables: {features}. Set rowKey to a field name, an array of field names, or a function.
  • rowKey.throw rowKey function threw; falling back to index identity for the affected rows.
  • rowOrder.set.shape rowOrder.set() takes an array of row keys; the call was ignored.
  • rowOrder.source a manual row order needs a source that orders rows in memory; this grid sorts elsewhere, so the order was not changed.
  • rows.forEachAll.partial rows.forEachAll walked only the rows this source has loaded: a remote or paged source holds the current page, not the whole set. Total on the server, or use a memory source.
  • rows.forEachExcept.unsupported rows.forEachExcept fell back to the filtered rows: this source cannot exclude one column's filter, so the result is narrowed by the column you asked to leave out.
  • rows.leavesOf.unsupported rows.leavesOf() needs a source that groups in memory; this grid groups elsewhere, so it returned no rows.
  • selection:outside-predicate row "{key}" is outside the filter captured when every matching row was selected, so it cannot be added to that selection and the selection is unchanged. Select all again under the current filter, or clear the selection and pick rows by key.
  • selection:predicate-partial selection.rows() was read while every row matching a filter is selected on a source that holds only the rows it has loaded, so it returned those loaded rows, not the whole selection. Use selection.stream() for every selected row and selection.count() for how many there are.
  • selection:predicate-partial-keys selection.keys() was read while every row matching a filter is selected on a source that holds only the rows it has loaded, so it returned those loaded keys, not the whole selection. Use selection.stream() for every selected row and selection.count() for how many there are.
  • selection:predicate-unsupported edit.deleteRows() was called with every row matching a filter selected on a source that holds only the rows it has loaded. The grid deletes by row key and has no key list for that selection, so it deleted nothing rather than only the loaded rows. beforeDelete carries the selection as `predicate` and `count`: delete by that filter on the server, then reload.
  • selection.unstable selection is being tracked by index because no rowKey is configured; it will not survive a sort, filter or reload.
  • shadow.history.window.kind:* a 'history' shadow's 'window' only accepts {kind: 'time', span}; '{kind}' is not honoured (a plain count is already what 'depth' means, and a session has no span to divide into buckets), so it reported the original, unwindowed history.
  • shadow.history.window.span:* a 'history' shadow's time window needs a positive 'span' (ms) or 'minutes'; it had none, so it reported the original, unwindowed history.
  • source.context.unserialisable config.context is not JSON-serialisable; cache keys ignore it.
  • source.derived.cycle a derived grid is deriving from itself, directly or through a chain. The cycle was refused; check the `from` of each grid in the chain.
  • source.derived.follow.* unknown follow "{mode}"; using "filtered". Known: {list}.
  • source.derived.from source.mode "derived" needs `from`: the grid to derive from.
  • source.derived.producer.conflict source.mode "derived": `profile` and `statistics` are both terminal producers and cannot be used together - each replaces the pipeline with its own output builder. Declare one, or chain a second derived grid whose `from` is the first.
  • source.derived.producer.ignored.* source.mode "derived": `{producer}` is a terminal producer and replaces the pipeline, so {keys} {is|are} ignored. Sort, filter or limit the derived grid itself, or chain a second derived grid whose `from` is this one.
  • source.derived.readonly a derived grid is read-only: its rows are computed from another grid. Write to the source grid instead, and the derived one follows.
  • source.derived.statistics.columns source.mode "derived": `statistics: { fn: 'correlation' }` needs `columns`: at least two column ids to correlate pairwise.
  • source.derived.statistics.fn.* source.mode "derived": unknown `statistics.fn` "{fn}". Known: {list}. Every *single-column* statistic is reached through `select` instead, e.g. `select: { p95: { of: 'amount', fn: 'p95' } }`.
  • source.derived.statistics.orient.* source.mode "derived": unknown `statistics.orient` "{orient}" for `fn: 'correlation'`; using "pairs". Known: pairs, matrix.
  • source.derived.statistics.series source.mode "derived": `statistics: { fn: 'series' }` needs both `of` (the column to summarise) and `by` (the column that orders it). `by` is never guessed - kernels see rows in arrival order, which is not the grid's sort.
  • source.derived.statistics.shape source.mode "derived": `statistics` needs an object naming the statistic, e.g. `{ fn: 'correlation', columns: ['a', 'b'] }`. Known fn: {list}.
  • source.derived.statistics.with source.mode "derived": `statistics: { fn: 'datasetVsDataset' }` needs `with`: the second grid to compare this one against.
  • source.derived.union.crossfilter source.mode "derived": `crossFilter` is not supported when `from` is a union of several grids - it has no single target once there is more than one parent. Push a filter onto the individual source grid you mean instead.
  • source.derived.union.cycle.* source.mode "derived": union source "{label}" was refused - it derives from the grid being derived, directly or through a chain ({path}). Remove it from `from`.
  • source.derived.union.failed.* source.mode "derived": union source "{label}" failed to read ({error}) and was skipped this pass.
  • source.derived.union.follow source.mode "derived": `follow` is ignored when `from` is a union of several grids - each entry carries its own `follow` instead.
  • source.derived.union.label.* source.mode "derived": two union sources share the label "{label}"; give each a distinct `label` or __source/__key will not distinguish them.
  • source.derived.union.missing.* source.mode "derived": union source "{label}" has no grid and was skipped.
  • source.derived.union.profile source.mode "derived": `profile` is not supported when `from` is a union of several grids; profile a single grid instead.
  • source.derived.union.statistics source.mode "derived": `statistics` is not supported when `from` is a union of several grids - a relational statistic reduces one grid's own columns, and a union has no single set of them. Derive the statistics of a single grid instead.
  • source.dfql.protected a DFQL update tried to write protected field(s) {fields}; they are server-assigned and immutable, so they were dropped from the PATCH.
  • source.dropped.* column "{colId}" cannot be served under ingest.dropSourceRows: it is an impure computed column (a shadow or a positional/rank column) that is never stored, so it can only be read from the caller's row objects
  • source.duckdb.close a prepared statement against "{from}" could not be closed. The query itself succeeded, so this is a resource leak in the engine connection rather than a wrong answer - but a connection that cannot close statements will eventually refuse to prepare new ones.
  • source.duckdb.count.unreadable the count statement against "{from}" returned no readable count, so the grid has no total for this query and will scroll open-ended rather than show the page size as the whole. This is an engine or driver result-shape problem, not a query problem.
  • source.duckdb.describe DESCRIBE of "{from}" failed ({error}), so filter values are typed from the grid column types alone. A timestamp or date filter on a column whose grid type is not declared may be refused by the engine.
  • source.duckdb.filter.refuse.* a "{op}" filter condition cannot be written as a DuckDB SQL predicate, so the request was refused rather than sent without it - silently dropping it would have matched more or fewer rows than the filter asked for. Route this condition through a source that can pushdown-split it as residual (source.filter capability), or drop it from the tree before building the SQL.
  • source.duckdb.list.column.* a "{op}" condition reached the DuckDB adapter for "{col}", which the engine does not describe as a LIST column; it matches no row rather than guessing.
  • source.duckdb.spatial.column.* column "{col}" is {type}, which is not a geometry DuckDB can read (GEOMETRY, or WKB in a BLOB), so spatial conditions on it match no row.
  • source.duckdb.spatial.crs.* the GeoParquet metadata does not say which CRS column "{col}" is in, so its coordinates are taken as longitude/latitude. Set `spatial.crs` if they are not.
  • source.duckdb.spatial.load the DuckDB spatial extension did not load ({error}), so spatial filters are evaluated in the browser over the rows DuckDB returns rather than pushed into it, and geometry arrives as the stored WKB. Correct, and slower: check the extension repository is reachable from this page.
  • source.duckdb.spatial.metadata the GeoParquet metadata of {path} could not be read ({error}), so its geometry is taken to be longitude/latitude unless `spatial.crs` says otherwise.
  • source.duckdb.spatial.module.* a "{op}" condition reached the DuckDB adapter, but no loaded data type declares "{op}" - the geometry module is not loaded (import '@toclocoinc/lattice-grid/modules/geometry'), so the grid refuses it and the adapter does not push it. It matches no row.
  • source.duckdb.spatial.operand.* a "{op}" condition was refused: {reason}. It matches no row rather than guessing.
  • source.duckdb.unprepared this DuckDB connection has no prepare(), so filter values cannot be bound. Filters are not sent rather than being interpolated into SQL.
  • source.duckdb.unprepared.aggregates this DuckDB connection has no prepare(), so filter values cannot be bound. Aggregates are not pushed rather than being interpolated into SQL.
  • source.duckdb.unprepared.group this DuckDB connection has no prepare(), so filter values cannot be bound. A grouped level is not fetched rather than having its values interpolated into SQL.
  • source.duckdb.unprepared.grouped this DuckDB connection has no prepare(), so filter values cannot be bound. Grouped aggregates are not pushed rather than being interpolated into SQL.
  • source.elasticsearch.analyzed.* "{name}" is a text field with no keyword sub-field, so a filter on it was pushed as a word-by-word match under the field's analyser (`match_phrase`) rather than a whole-value or substring match, and may keep or drop rows the same filter treats differently in the grid. Map a keyword sub-field for it, or filter that column in the grid.
  • source.elasticsearch.auth the cluster refused the credential; supply a valid one through `headers` (`Authorization: ApiKey …` or `Basic …`), or a `fetch` wrapper that sets it per request. The adapter holds no credential of its own, so there is nothing for it to retry with.
  • source.elasticsearch.cardinality a distinct count reached {value}, past the {CARDINALITY_PRECISION} values below which the engine's `cardinality` is near-exact, so the figure is an estimate. Compute it in the grid where an exact count matters.
  • source.elasticsearch.casefold a case-insensitive filter value holds a letter outside ASCII, and Elasticsearch's `case_insensitive` folds ASCII letters only, so the pushed filter may miss case variants of that letter the same filter keeps in the grid. Set `caseSensitive: true` to make the two agree, or give the field a lowercase normalizer.
  • source.elasticsearch.truncated the export stream was stopped after {maxResultRows} rows because `maxResultRows` is set to {maxResultRows}; the matching set may hold more than that. Raise `maxResultRows`, or drop it to read the stream unbounded.
  • source.graphql.nototal {url} was asked for totalCount and answered without one, so the grid has no total for this query and will scroll open-ended rather than present the page size as the whole. The schema may not expose totalCount on this connection; pass `count: false` to stop asking, or a `parseResponse` that reads whatever the schema does call it.
  • source.graphql.selection the graphql adapter was built without `fields` or `selection`, so the default query selects only `id`. Name the fields your grid shows, or pass a custom `buildQuery`.
  • source.group.* grouping ignores unknown column "{col}".
  • source.mode.* unknown source.mode "{mode}"; falling back to "memory". Known modes: {list}.
  • source.moveRow.dropped rows.move() is not supported under ingest.dropSourceRows: the source objects have been released and the store cannot reorder its columns in place. Enable retainSource (leave dropSourceRows off) to reorder rows.
  • source.odata.filter.refuse.* a "{op}" filter condition cannot be written as an OData $filter expression, so the request was refused rather than sent without it - silently dropping it would have matched more or fewer rows than the filter asked for. Route this condition through a source that can pushdown-split it as residual (source.filter capability), or drop it from the tree before building the URL.
  • source.odata.nocount {base} was asked for $count=true and answered without an @odata.count, so the grid has no total for this query and will scroll open-ended rather than present the page size as the whole. The endpoint does not support $count, or filters it out.
  • source.paged.fetch source.mode "paged" requires a fetch function; the grid will stay empty.
  • source.pipeline.sort off-thread sort failed; recomputed on the main thread.
  • source.pivot.kernel pivot requested but ctx.compute.pivot is not available; showing unpivoted rows.
  • source.pushdown.adapter createPushdownSource needs an `adapter` with an `execute(query)` function.
  • source.pushdown.aggregates.** the {adapter} adapter cannot compute {grouped }aggregates, so every requested statistic is computed client-side. Correct, and slower than it needs to be.
  • source.pushdown.group-unsupported.*.* pushdown:group-unsupported: the {adapter} adapter was not asked to group this query because {reason}, so the grid stays ungrouped rather than building group rows, counts and subtotals from a window of leaf rows. `lastPlan().groupReason` says the same.
  • source.pushdown.group.*.* the {adapter} adapter was not asked to group this query because {reason}. The engine returns rows and the grouping is the grid's to do, which needs every matching row in the browser - correct where the grid holds them, and wrong-looking where it holds a window. `lastPlan().unpushed` names it.
  • source.pushdown.group.aggregates.method.* the {adapter} adapter declares `aggregates` but has no `executeGroupedAggregates(query, groupBy, aggregates)`, so the grouped view cannot be built from grouped aggregates and the grid stays ungrouped unless it holds the whole matching set. Implement the method, or declare `group` and implement `executeGroupLevel`.
  • source.pushdown.group.method.* the {adapter} adapter declares the `group` capability but has no `executeGroupLevel(query, aggregates, request)`, so grouping is not pushed and the grid stays ungrouped unless it holds the whole matching set. Implement the method or drop the capability.
  • source.pushdown.group.totals.*.* the {adapter} adapter cannot compute the group subtotal for {columns}, so those group rows carry no value for that column. A figure computed over anything other than the group would be wrong, so none is shown; `lastPlan().aggregates.client` names them.
  • source.pushdown.group.unfiltered.* the {adapter} adapter could not count the unfiltered set ({error}), so "N of M" falls back to the display count for its denominator.
  • source.pushdown.partial.* the {adapter} adapter returned {n} of {total} matching rows when asked for the whole result. The adapter must follow the engine's own paging before returning.
  • source.pushdown.partial.residual.* the {adapter} adapter returned {n} of {total} matching rows when asked for the whole result, and residual work will run over that fraction. allowPartialResults is set, so the result is used as-is - it may be wrong. The adapter must follow the engine's own paging to be correct.
  • source.pushdown.pivot.*.* pushdown:pivot-unsupported: the {adapter} adapter cannot pivot this query because {reason}, so no rows are fetched rather than every leaf row being pulled into the browser to pivot there. `lastPlan().pivotReason` says the same.
  • source.pushdown.pivot.totals.*.* the {adapter} adapter cannot compute the pivot cells for {columns}, so those cells are empty. A figure computed over anything other than the cell would be wrong, so none is shown; `lastPlan().aggregates.client` names them.
  • source.pushdown.readonly.* edit is enabled but the {adapter} adapter declares mutate: {false|without update} (read-only by declaration), so no commit is synthesised and the grid stays read-only. Declare `mutate: { update: true }` on the adapter to allow cell edits.
  • source.pushdown.residual.*.* the {adapter} adapter cannot push {work}, so every matching row is fetched and the rest is applied in the browser. Correct, and slower than it needs to be: widening the adapter is the fix.
  • source.relative.* unknown relative date token "{token}"; sending the condition unresolved.
  • source.remote.fetch source.mode "remote" requires a fetch function; the grid will stay empty.
  • source.remote.hostFilter config.hostFilter is not sent to a server-delegated source (paged/remote) and is not applied: filtering only the fetched window would give wrong counts and totals. Express the filter as a FilterSet condition, or send it through config.context.
  • source.rowKey.missing no config.rowKey; falling back to index identity. Row changes, streaming dedupe, selection persistence and remote reload are disabled.
  • source.sort.* sort ignores unknown column "{col}".
  • source.sort.running.* sort ignores "{col}": a running total is a function of the sort order, so it cannot also decide it. Sort by the column it runs over instead.
  • source.splunk.auth Splunk refused the token; supply a valid token through `headers` (or a `fetch` wrapper that sets Authorization per request). The adapter holds no credential of its own, so there is nothing for it to retry with.
  • source.splunk.casesensitive a filter asked for a case-sensitive comparison and Splunk's search matching is case-insensitive, so it was pushed without the case constraint and the rows may include case variants the same filter excludes in the grid. Drop `caseSensitive` to make the two agree, or filter that column in the grid.
  • source.splunk.failed Splunk refused a request with HTTP {status} and gave its own reason: {detail}. The error thrown from this call carries the same detail.
  • source.splunk.truncated Splunk truncated this search at {count} results, so the grid's total is the truncated count and not the number of events that matched. Narrow the search, raise `maxresultrows` in the instance's limits.conf, or read the result in `mode: "export"`.
  • source.stream.columnize worker columnization failed; columnizing on the main thread
  • source.stream.ingest-worker stream ingest.useWorker is on but a column reads through a closure (a computed or date column); columnizing on the main thread instead.
  • source.stream.open source.mode "stream" requires an open function; the grid will stay empty.
  • source.stream.remove removing rows from a live stream is not supported; reload instead.
  • source.stream.testValue ctx.compute.testValue is unavailable; streamed rows are not filtered client-side.
  • source.url.badline.* skipping a malformed NDJSON line (line {n}) from {url} - {error}
  • source.url.url createUrlSource needs a URL string; the grid will stay empty.
  • source.where.notApplied:* filters.where("{name}") is registered but the {mode} source holds only its fetched window and cannot run the predicate over it: the predicate is not applied there, and rows it would exclude stay on screen. Give it a { condition } twin so the engine can narrow the fetch itself - that is the supported route for a paged or remote source.
  • source.where.overThreshold:* filters.where ({names}) is not applied to the {adapter} pushdown source: {the size of the matching set is unknown|its matching set is N rows, at or past the whereRowLimit}. Running the predicate needs every matching row fetched and held here, which is the whole download a pushdown source exists to avoid, so it is refused and the rows the predicate would exclude stay on screen.
  • source.where.pageRelative filters.where ({names}) runs over the rows the {mode} source fetched, not the whole dataset: match counts and totals are page-relative. Give the predicate a { condition } twin so the engine can narrow the fetch itself.
  • stat.pivot-unsupported:profile:* the profile of `{col}` cannot be computed over a pivoted pushdown grid (source #{n}): a pivot keeps no leaf rows client-side, so it reports no profile rather than a leaf-window figure.
  • stat.pivot-unsupported:reduce:*:* `{stat}` on `{col}` cannot be computed over a pivoted pushdown grid (source #{n}): a pivot keeps no leaf rows client-side, so it reports no number rather than a leaf-window figure.
  • stat.windowed:*:* `{stat}` on `{col}` (source #{n}) computed over {covered} of {total} matching rows (windowed source); hold the data in a memory source for whole-dataset statistics.
  • store.dup.* duplicate column id "{id}" in the store schema; ignoring the second
  • store.kind.* column "{id}" declares unknown storage kind "{kind}"; falling back to object
  • store.null.* column "{id}" is declared non-nullable but received null; storing a filler value
  • store.set.* set() on unknown column "{colId}"
  • total:* unknown total function "{fn}"; register it in config.totalFns
  • total.unsafe:*:* column "{id}" is typed {type}, for which the total "{fn}" is not meaningful. Meaningful: {list}. Ignored.
  • tree.cycle tree.parentKey produced a cycle; the affected rows were attached to the root.
  • tree.grouped grouping is active on a grid configured with `tree`; the grouping wins and the hierarchy is not shown.
  • tree.loadChildren tree.loadChildren failed for "{key}": {error}
  • tree.path.duplicate two rows share a tree.path; the later ones were nested beneath the first.
  • tree.path.empty tree.path returned an empty path; the affected rows were placed at the root.
  • tree.path.throw tree.path threw; the affected rows were dropped from the hierarchy.
  • tree.shape tree is configured with neither `path` nor `parentKey`; rows stay flat.
  • type:* unknown data type "{type}"; falling back to "text"
  • type:of:* column "{col}" names "{name}" as its item type (typeOptions.of), which is not {what}; its items are read as text.
  • undeclared:*:* computed column "{colId}" read undeclared dependency "{field}". Add it to `value.deps` or the value will go stale.
  • unit:compound:*:* unknown compound unit "{symbol}" in system "{system}"; ignoring it
  • unit:display:* unknown display unit "{symbol}"; rendering in {base}
  • unit:system:* unknown unit system "{system}"; known systems are {list}
  • unit:unit:*:* unknown unit "{unit}" in system "{system}"; falling back to {base}
  • views:read saved views could not be read: {error}
  • views:write saved views could not be persisted: {error}

The grid (rendering) (92)

  • annotate.add.invalid annotate.add ignored a mark: it needs a known `type` ({types}) and at least two finite {x, y} points in content coordinates.
  • annotate.remove.unknown annotate.remove ignored an unknown id: "{id}".
  • autoHeight-limit autoHeight is off above {n} rows; use autoHeight: 'visible' to measure only rendered rows
  • barcode:invalid:* column "{colId}" has a value that is not valid for the "{type}" barcode symbology (bad characters, wrong length or a failed check digit); showing a placeholder instead.
  • board.empty config.board is on but the grid has no columns and no template, so a card would draw nothing. Supply board: { template: '<div>{{cell.name}}</div>' } or add columns.
  • cell-type:* Column '{colId}' cell must be an object or a renderer name.
  • celledit.noeditor:* column "{colId}" has no editor.
  • celledit.offscreen cannot edit a row that is not rendered; scroll to it first.
  • class-fn:* Column '{colId}' cell.class threw.
  • classwhen-fn:*:* Column '{colId}' classWhen['{class}'] threw.
  • classwhen-type:*:* Column '{colId}' classWhen['{class}'] must be a function or a rule string.
  • classwhen:*:* Column '{colId}' classWhen['{class}'] could not be read as a rule: '{text}'. Use a function, or a rule such as 'lt 0', '>= 1000', 'eq Open', 'contains urgent', 'blank'. Valid operators (spec 9.3): {list}.
  • columns.pinned-overflow pinned columns need {n}px of a {m}px viewport, so the {k} unpinned column(s) have no room and cannot be seen. Unpin some, narrow them, or widen the grid.
  • css-escape:* Column '{colId}' uses cell.css, which writes style properties per cell and leaves the class fast path. It is unsuitable for large datasets; prefer a decoration and variant.
  • date-editor:time-format:* column "{colId}" is type "{type}" but its format shows a time. The editor is including one so the time is not overwritten with midnight, but the column should be `type: "datetime"`: `date` means a calendar day.
  • dec-conflict:* Column '{colId}' sets both a decoration and a {renderer|template}. A {renderer|template} owns the cell's content, so the decoration is ignored. Use cell.classWhen alongside a template instead.
  • dec-icon:* Column '{colId}' uses decoration 'icon' without a 'name', 'bands' or 'iconSet'. Give an icon name, a value->name map, a bands list, or a built-in iconSet ({sets}).
  • dec-iconset:*:* Column '{colId}' uses unknown iconSet '{name}'. Valid sets: {list}.
  • dec-name:*:* Column '{colId}' uses unknown decoration '{type}'. Valid decorations (spec 8.7): {list}.
  • dec-shape:*:* Column '{colId}' decoration shape '{shape}' is not one of pill, rounded, square.
  • dec-size:*:* Column '{colId}' decoration size '{size}' is not one of sm, md, lg.
  • dec-type:* Column '{colId}' has a decoration of unsupported type '{type}'.
  • destroy:* Column '{colId}' renderer threw during destroy().
  • detail.rows:failed detail.rows() rejected for row "{key}": {error}
  • detail.target:* detail.target "{selector}" matched no element; details will not be shown.
  • diff-before-format:* the formatter for column "{colId}" threw while formatting a previous value, so the diff hover shows the raw one.
  • editbar.noeditor:* column "{colId}" has no editor.
  • editor:missing:* unknown editor "{name}"; falling back to the data type default
  • filter:missing:* unknown filter "{name}"; falling back to the data type default
  • filter:regex:* invalid regular expression in a "matches" filter: {pattern}
  • filter:values-unsupported:* the filter on column "{colId}" cannot list its values: {reason}. It offers the text condition editor instead.
  • filtermenu:* column "{colId}" has no filter to open.
  • fullWidth.render.missing config.fullWidth.when is set but config.fullWidth.render is not; full-width rows are off. Supply render(params) returning a string or a node.
  • fullWidth.render.threw config.{fullWidth.render} threw: {error}; the row is left empty.
  • fullWidth.when.threw config.fullWidth.when threw: {error}; the row is rendered normally.
  • gallery.empty config.gallery is on but the grid has no columns and no template, so a tile would draw nothing. Supply gallery: { template: '<div>{{cell.name}}</div>' } or add columns.
  • groupRenderer.threw config.{groupRenderer} threw: {error}; the row is left empty.
  • header-name:*:* {kind} '{id}' names header renderer '{name}', which is not registered. Register it in config.components.
  • header-render:* the header renderer for {kind} '{id}' threw; the stock heading is shown instead.
  • height.autoHeight-and-rowHeight both `autoHeight` and a `rowHeight` function are set; `autoHeight` wins and the function is not used for row geometry. Drop one.
  • hydrate.unknownType:* data-type "{declaredType}" on column "{field}" is not a built-in type name, so it was ignored; the column falls back to sampled shape typing.
  • icon:* Unknown icon '{name}'; drew a blank glyph in its place. Register it with grid.icons or config.icons.
  • icon:empty:* An empty icon value at {key} renders nothing; omit `icon` instead.
  • icon:type:* An icon must be a name, a character, or element markup; got {type} at {key}.
  • maximise.detached the grid cannot be maximised because its element is not in the document.
  • menu-layer:* {where} must be a boolean, an array of items or a (params, defaults) => items function; got {type}. Ignoring it.
  • pipe:*:* Column '{colId}' template uses unknown pipe '{name}'. Built-in pipes: {list}. Register others with config.pipes.
  • presentation.conflict More than one card presentation is set ({list}); only {first} draws. Set exactly one of rowTemplate, gallery, recordCard, board or pivotView.
  • qrcode:capacity:* column "{colId}" has values too long for a QR code at level {level}; the maximum this build encodes is {n} bytes (version {v}).
  • qrcode:level:* unknown QR error-correction level "{level}"; using M. Valid levels are L, M, Q and H.
  • recordCard.empty config.recordCard is on but the grid has no columns and no template, so a card would draw nothing. Supply recordCard: { template: '…' } or add columns.
  • render-escape:* Column '{colId}' cell.render returned markup, which was written as text. Set allowUnsafeTemplates: true at grid level to insert it as HTML, or return an HTMLElement.
  • render-fn:* Column '{colId}' cell.render threw; the cell falls back to its formatted text.
  • render-name:*:* Column '{colId}' names renderer '{name}', which is not registered. Register it in config.components, or use a built-in: group, checkbox, progress, link, pill, icon, skeleton.
  • render-type:* Column '{colId}' cell.render must be a name, a function or a class.
  • resize-observer ResizeObserver is unavailable; the grid will not follow container size changes
  • responsive.missing config.responsive is set but carries no template; the grid stays a table at every width. Supply responsive: { maxWidth: 640, template: '<div>{{cell.name}}</div>' }.
  • rowform:container rowForm.container could not be resolved; the form opens over the grid instead.
  • rowform:create:missing-key rowForm could not add the new row: it carries no row key. Return the stored row, key included, from rowForm.create, or give the key a field or a default.
  • rowform:no-fields rowForm has no fields to show; the form opened with no controls. Check rowForm.fields, or the grid's columns when fields is absent.
  • rowform:off grid.form.openNew() was called on a grid with no row form (rowForm is off, or no renderer is attached); nothing opened.
  • rowTemplate.missing config.rowTemplate is set but carries no template string; the card presentation is off. Supply rowTemplate: '<div>{{data.name}}</div>' or { template: '…' }.
  • rowTemplate.protected a card template binds {fields} on a protected column; the masked text is shown instead. Use {{cell.<column>}} to show what the grid shows.
  • scrollbars-mixed-custom scrollbars is '{x}' on x and '{y}' on y. Drawing one axis hides the native scrollbar on both, because no browser lets one axis be hidden on its own, so the other axis will scroll with no bar. Set scrollbars: 'custom' on both axes, or on neither.
  • scrollToColumn:* scroll.toColumn: no visible column '{id}'
  • span-lookback:* column '{colId}' spans {n} rows, more than the {limit}-row span look-back; it will be clipped once its origin scrolls past that distance
  • stat.container createStat needs a `container`: an element or a selector.
  • stat.document createStat needs a document; it draws DOM.
  • stat.show.* `show` needs `fn: 'min'` or `fn: 'max'`: "{fn}" reduces to a value that no single row holds, so there is no row to report from.
  • template-binding:*:* column '{colId}' has a template binding {{ {name} }} that resolves to nothing and will render empty. Bindings are value, text, index, colId, data.*, row.* and anything in cell.props, a field is reached as {{ data.{root} }}.
  • toolpanel:* unknown tool panel "{name}"
  • tooltip-content:* Column '{colId}' cell.tooltip render returned a value that is not an element, a { title, rows, note } spec, a { html } wrapper or a string, so nothing was shown.
  • tooltip-mount:* Column '{colId}' cell.tooltip mount threw; the tooltip was closed.
  • tooltip-render:* Column '{colId}' cell.tooltip render threw; no tooltip was shown.
  • tooltip-unmount A cell.tooltip unmount threw while the tooltip was closing.
  • tpl-attr:*:* Column '{colId}' template sets '{attr}'. Inline event handlers are not allowed in templates; use grid events or a component renderer. The attribute was dropped.
  • tpl-control:* Column '{colId}' template looks like it contains control flow. Templates have none by design: use cell.classWhen for conditional styling and a function renderer for conditional content.
  • tpl-open:* Column '{colId}' template has an unclosed '{{'. The rest is treated as text.
  • tpl-tag:*:* Column '{colId}' template contains <{tag}>, which is not allowed in a cell template. It was dropped.
  • tpl-unsafe:* Column '{colId}' template uses {{{{expr}}}} (unescaped output), which requires allowUnsafeTemplates: true at grid level. The value will be escaped instead. Turning escaping off must be a deliberate, auditable choice.
  • tpl-url:*:* Column '{colId}' template uses a '{attr}' with a refused scheme. The attribute was dropped.
  • variant-def:*:* (the specific problem found in the definition: a missing theme, a missing role, or a contrast failure with the measured ratio)
  • variant-fn-token:*:* Column '{colId}' variant function returned unknown token '{token}'; falling back to '{fallback}'.
  • variant-fn:* Column '{colId}' variant function threw; falling back to '{fallback}'.
  • variant-op:*:* Column '{colId}' variant.when[{i}] uses unknown operator '{op}'. Valid operators (spec 9.3): {list}.
  • variant-override:* Custom variant '{name}' overrides a built-in token. The built-in colours are contrast-checked in both themes; the override is now your responsibility.
  • variant-shape:* Column '{colId}' has a variant object with neither 'map' nor 'when'. Use a token string, { map }, { when } or a function.
  • variant-type:* Column '{colId}' has a variant of unsupported type '{type}'.
  • variant:*:* Column '{colId}' {where} uses unknown variant '{token}'. Register it with config.variants (light and dark together) or use one of: {list}.
  • views:local:ignored views.local is ignored because views.storage was also supplied; the explicit adapter wins.
  • views:local:parse saved views under localStorage key "{key}" could not be parsed and were ignored: {error}
  • views:local:unavailable localStorage is not reachable; a grid configured with views.local will not persist views across reloads.

Charts (79)

  • chart:*:animation:invalid {problems}. The default is used in its place.
  • chart:*:annotate:projection "annotate" anchors a drawing on a {type} in longitude and latitude, and this map is drawn through a projection with no written inverse, so no rail was added. Draw it through projection: 'mercator' or 'equirectangular' to annotate it.
  • chart:*:annotate:type:* "annotate" adds a drawing rail to a chart with two readable axes and {type} has none, so no rail was added. The types that take one are {types}.
  • chart:*:brush:aggregate a brush on the {axis} axis would filter rows by "{column}", but the marks on that axis are totals over several rows (or counts), and a range of totals says nothing about which rows lie in it, so no filter was written. Brush the x axis, or chart one row per mark.
  • chart:*:brush:measures a brush on the {axis} axis filters the measure behind it, and this chart has {count} measure column(s) there ({columns}), so no filter was written. Brush an axis that carries one measure.
  • chart:*:brush:polar:* a {type} chart can only brush to a selection, and brush: {mode} was set, so the drag did nothing. Set brush: 'select' to select the rows inside the dragged band in the grid.
  • chart:*:cursor:groupTooltips "cursor.groupTooltips" is {value}, which is not 'pointer' or 'all', so 'pointer' was used: only the chart under the pointer shows its tooltip.
  • chart:*:cursor:multiples "cursor" is not drawn on small multiples: each panel has its own plot and the set reads as one chart, so no cursor was added. Put a cursor on each panel's own chart instead.
  • chart:*:cursor:off chart.cursor.set() was called on a chart with no cursor, so nothing moved. Set `cursor: true` on a cartesian chart before driving it.
  • chart:*:cursor:type:* "cursor" is a crosshair over a chart's own axes and {type} has none to put one on, so no cursor was added. The types that take one are {types}.
  • chart:*:empty chart bound nothing from the {n} row(s) the grid is showing, so it shows its empty state. The binding named no series to draw: check that the spec names the columns this chart needs - a flow type needs source and target, a measure needs a column with values in it.
  • chart:*:geometry:both a "{type}" chart was given both "geometry" and "lon"/"lat", and a row is placed by one or the other, so nothing was drawn rather than one being picked for you. Keep "geometry" for a geometry column, or "lon" and "lat" for two coordinate columns.
  • chart:*:geometry:missing a "choropleth" chart draws each row's own shape from a geometry column and was given none, so it has nothing to draw. Name the column: geometry: '<column>'.
  • chart:*:geometry:type:* a "{type}" chart reads "geometry" from column "{col}", which is not a geometry column, so no row could be placed. Import '@toclocoinc/lattice-grid/modules/geometry' and declare the column type: 'geometry'.
  • chart:*:incomplete a candlestick chart needs four measures - open, high, low and close, in that order - and only {n} were bound. Pass them as measures: [open, high, low, close].
  • chart:*:missing-column chart {key}: "{col}" names a column this grid does not have, so there is nothing to read and nothing to draw. The columns it does have: … Bind a column the grid declares, or declare the column.
  • chart:*:no-domain chart x column "{col}" is drawn on a {time|continuous} axis, and not one of its {n} value(s) reads as a {date|number} - so the axis would be drawn over a domain invented from nothing rather than measured from the data. Remove axis.x.scale to let the column choose its own scale, or type the column so its values parse.
  • chart:*:notice:* {notice}
  • chart:*:ramp:log chart {type} is coloured on a log ramp scale: its values span {ratio}:1, past the 100:1 at which a linear ramp leaves all but the largest in its palest shade. Set ramp: { scale: "linear" } to keep it linear.
  • chart:*:rangearea:form a rangeArea chart fills the band between two measures and was not given two. Write `measures: [low, high]` (low first, then high), with an optional third measure typed `line` for the centre: `measures: ['avg', 'peak', { col: 'mean', type: 'line' }]`. `y` and `y2` do not form a band, so this drew a plain area.
  • chart:*:viewportFilter:globe a globe's "viewportFilter" writes the visible hemisphere as one condition on a geometry column, and this chart places its rows by "lon"/"lat", so no filter was written. Place the rows with geometry: '<column>' (or name viewportFilter: { column }).
  • chart:*:viewportFilter:y "viewportFilter" filtered the grid by the x range only: this {type} chart's measure axis shows values combined over several rows at one x rather than any one row's value, so its y zoom narrows the picture but not the grid. A scatter or bubble with one row per point filters by y as well.
  • chart:*:viewportFilter:zoom "viewportFilter" on a {type} chart filters the grid to the range the chart is zoomed to, and this chart has no zoom, so the grid was left alone. Set zoom: true as well.
  • chart:*:volume:missing-column "volume" names {col}, which is not a column of this grid, so the chart was drawn without a volume pane. Name the column that holds each period's traded volume by its id.
  • chart:*:volume:type:* "volume" adds a volume pane under a candlestick's (or an ohlc's) price pane, and this chart's type is {type}, so no volume pane was drawn. Set type: 'candlestick' or 'ohlc', or draw the volume as its own bar chart.
  • chart:*:volumePane:height "volumePane.height" is the volume pane's share of the plot's height, a number above 0 and below 1, and was given {height}, so the default of {default} was used.
  • chart:*:x:array chart "x" expects one column id (a string) and was given an array [{ids}]. A chart takes one category column on "x"; only the hierarchical types (pie, donut, sunburst, treemap) read an array as their levels.
  • chart:*:x:window:ahead {n} reading(s) on the x axis{ column} were not drawn: stamped up to {lead} ahead of this device's clock, which is more than the {limit} ({share} of the {span} rolling window) a reading may run ahead and still move the window. This is a producer whose clock is wrong, not a window that is too narrow
  • chart:*:x:window:stale:* the rolling x window covers the last {span} ms, and the newest of {n} x value(s) is {age}s old, so every mark would be drawn outside the plot. The chart shows its empty state instead. Seed it with data inside the window, widen axis.x.window.span, or drop the window while showing history.
  • chart:*:zoom:heatmap:axes a heatmap zooms its columns (the x axis) only; its rows are a band of series with no measure axis behind them, so zoom.axes {value} zoomed x alone. Leave zoom.axes at its default, x.
  • chart:*:zoom:off chart.zoom.set() was called on a chart with no zoom, so nothing moved. Set `zoom: true` on a cartesian chart (or `zoom: { buttons: "none" }` to drive it from your own controls) before setting a range.
  • chart:*:zoom:type:* "zoom" narrows a chart's own axes and {type} has none to narrow, so no zoom was added. The types that take one are {types}.
  • chart:aggregate:engine-refused:*:* chart aggregate: 'engine' was refused: {reason}. The chart reads the rows the grid has loaded instead; chart.provenance() reports the same reason.
  • chart:aggregate:mode:* chart aggregate {value} is not a mode; use one of {modes}. The chart used 'auto'.
  • chart:aggregate:window:*:* this chart draws the rows the grid has loaded, which are a window of the matching set, because {reason}. Its figures describe that window, not the whole relation.
  • chart:annotate:anchors:* a drawn chart annotation of kind {kind} was not restored: it needs {needed} data anchor(s) and carried {given}.
  • chart:annotate:kind:* a drawn chart annotation of kind {kind} was not restored: the kinds are {kinds}.
  • chart:annotate:tools:* chart annotate.tools names {count} tool(s) this chart does not have: {names}. The tools are {tools}.
  • chart:annotate:tools:legacy:* chart annotate.tools lists {names}, which {verb} deprecated: the rail has one "measure" tool now. Use "measure" with measure: { axes: 'x' | 'y' | 'xy' }.
  • chart:annotation:dropped:*:*:* chart annotation{ (label)} of kind "{kind}", reading the {axis} axis, was not drawn: {reason}.
  • chart:editable:aggregated a chart mark that stands for several rows (a bar summing a category, a group) cannot be dragged, because there is no single cell to write. Set editable: { distribute: "proportional" } (or "first") to say how a new total is shared between its rows, or chart one row per mark.
  • chart:editable:axis editable.axis asks for an axis this chart cannot drag along. Only a numeric x axis (a scatter or bubble) can be dragged sideways; a bar, line, area, floating bar, pie or donut moves along its value only. Use axis: "y".
  • chart:editable:cap editable draws a handle on at most 2000 marks, and this chart has more, so the rest cannot be dragged. Filter the grid, or aggregate the chart, to a size a person can point at.
  • chart:editable:distribute editable.distribute shares a new total between the rows of a mark, and only a sum or an average has a total to share. A mark that is a minimum, a maximum, a median or a count cannot be dragged.
  • chart:editable:type editable has no effect on this chart: its type has no draggable marks, it is stacked or percent (no single value at the end of a mark), it is drawn as small multiples, or it is a floating bar on a time axis. It drags a line, step or area point, a bar or horizontal bar end, both ends of a floating bar, a scatter or bubble point, and a pie or donut slice edge.
  • chart:fill:invalid:* a chart fill object was ignored: {detail} The mark was drawn in a solid colour instead. A fill is a gradient object (gradient: "linear" or "radial", with stops of [offset, colour]) or a pattern object (pattern: "hatch", "crosshatch", "dots" or "grid", with a colour).
  • chart:floating:y-pair a floating bar needs a from/to pair of columns, given as y: [lowCol, highCol]. It was given {y}, so it drew nothing.
  • chart:geomap:pack:* geometry pack "{name}" has not been loaded, so the map fell back to the schematic continents. Import the pack module and pass it as `shapes`
  • chart:geomap:projection:* (the projection resolver's own explanation of why the named projection could not be used)
  • chart:grid-destroyed this chart was bound to a grid that has been destroyed, so it draws nothing. Point it at a replacement with chart.rebind(grid).
  • chart:icon:* Unknown icon '{name}' on {who}; {outcome} instead. Register it with registerIcon or config.icons, or name a built-in.
  • chart:icons:* {Who} asked for the icon '{name}', but this grid publishes no icon registry - a headless grid has no sprites. The {thing} drew as {outcome}.
  • chart:marker:image-failed:* a chart marker image could not be loaded ({shown}); those points keep the default marker. Check the URL, and that the server allows the page to load it.
  • chart:marker:no-image chart "marker" needs an "image": an icon name, a URL, { col } or a function. No markers were drawn from it.
  • chart:marker:unsafe-url:* a chart marker image URL was refused ({shown}): only http, https, blob, relative and data:image URLs are drawn. Those points keep the default marker.
  • chart:measure:fn:* chart measure fn {fn} is not a known aggregation; using '{default}'. Supported: {list}.
  • chart:pyramid:halves a populationPyramid needs exactly two halves: a y column split by a series column holding two values, or measures: [left, right]. It found {count}, so it draws the first two.
  • chart:ramp:diverging-scale chart "ramp.scale" was "{scale}" on a diverging ramp, which is always linear and centred on zero; the scale was not applied. Drop "diverging" to colour by a log or quantile scale.
  • chart:ramp:scale:* chart "ramp.scale" was "{scale}", which is not a ramp scale; the chart chose one from its values instead. Use "linear", "log" or "quantile".
  • chart:requires-core the charts module needs the Lattice core; load lattice-grid (or its script build) before modules/charts.
  • chart:responsive-option the chart option `responsive` takes 'auto' or an array of rules ({ maxWidth?, minWidth?, maxHeight?, minHeight?, set }); it was ignored.
  • chart:responsive-rule a chart `responsive` rule was ignored: each rule needs a `set` object and, optionally, numeric maxWidth / minWidth / maxHeight / minHeight.
  • chart:rows:not-grid-rows chart "rows" takes rows the grid holds (from grid.rows.get(i) or grid.rows.byKey(key)); {left} of {given} given are not and were left out. To chart other data, put it in a grid and chart that grid.
  • chart:scheme:array-and-map chart "scheme" was given as a name map together with a "palette" array; the two do not combine. Colours came from the map and the array was ignored. Pass one or the other.
  • chart:viewportCap:* chart viewportCap {value} is not a positive whole number of rows; the map used {default}.
  • chart:viewportCap:over:* this map's view holds {count} matching rows, more than its viewportCap of {cap}, and {reason}, so it draws none of them. Zoom in, narrow the filter, or raise viewportCap.
  • chart:x:banded:* chart x column "{col}" is typed "{type}", so its {n} values are drawn as categories on a band scale even though they all read as {dates|numbers}. Type the column {'date'|'number'} for a continuous axis, or pin it with axis: { x: { scale: '{kind}' } }.
  • chart:x:date-bucket:* chart x column "{col}" is a 'date', which stores a calendar day, so all {n} rows collapsed into one point and were reduced by '{fn}'. For a series inside a single day, type the column 'datetime' or bind epoch milliseconds.
  • chart:x:scale:unknown:* chart axis.x.scale "{scale}" is not a scale this chart draws. Use one of: {list}. The scale was chosen from the column instead.
  • chart:zoom:axes:* chart zoom.axes is {value}, which is not an axis this chart can zoom; it was ignored and x was used. The values are {values}.
  • chart:zoom:buttons:* chart zoom.buttons is {value}, which is not a way to show the zoom toolbar; it was ignored and hover was used. The values are {values}.
  • chart:zoom:range a chart zoom range named a value this chart's axis does not have, so the zoom was left alone. An x range on a category axis names two categories; on a continuous or time axis it is two readings, and a measure range is two values.
  • chart.axis.band.every.impossible axis.x.every/axis.y.every is set to {every}, which on {total} band(s) never shows a second label no matter how much room the axis is given. Lower it, or drop it to let the axis thin automatically.
  • chart.axis.window.band a rolling time window needs a continuous x axis; this chart bands its x values, so the window is ignored. Bind a date or numeric column to x.
  • chart.axis.window.kind axis window kind "{kind}" is not supported on a chart axis; only { kind: "time", span } rolls the domain. Bound the row count with the source's maxRows instead.
  • chart.axis.window.now axis.x.window.now did not return a time, so the rolling window is measured from this device's clock instead. It must return epoch milliseconds - the same number Date.now() returns - because that is what the x values are compared against.
  • chart.axis.x.domain.beyond axis.x.{min|max} is outside the x values entirely, so the plot is empty. The pin was kept and the other edge moved to hold the axis width; check the units (a time axis is pinned in epoch milliseconds).
  • chart.axis.x.domain.inverted axis.x.min ({min}) is not below axis.x.max ({max}), which is a mirrored axis rather than a narrow one, so both were ignored and the x domain was taken from the data. On a time axis both are epoch milliseconds.
  • chart.format.string:*:* {option}: the format string "{format}" is not understood; values are drawn unformatted

Data Router (43)

  • data-router:bad-query-source router.query(adapter) needs an adapter with an execute(query) function (a DFQL/DuckDB adapter or a pushdown source); nothing was fetched.
  • data-router:bad-target attach/subscribe target has no rows.apply and is not a function; its slice goes nowhere. Pass a grid or a handler(change) function.
  • data-router:broadcast-channel router.broadcast({ channel }) needs a string channel name; nothing was mirrored.
  • data-router:buffer-default-cap buffer() called with no window or max; applying a default cap of {n} deltas so the buffer stays bounded. Pass { window } or { max } to size it.
  • data-router:collect-select-field:* a collect join's `select` names "{name}", which the collected rows do not have; it is collected as undefined.
  • data-router:devtools-target router.mountDevtools(el) needs a router with metrics()/on() and a DOM element to render into; the panel was not mounted.
  • data-router:duplicate-source addSource("{id}") was already registered; returning the existing source handle. Use a distinct id per feed.
  • data-router:field-spec addSource field "{field}" has an unusable fields declaration (a type string or an options object); the field passes through uncoerced.
  • data-router:field-type addSource field "{field}" names an unknown type "{type}" (expected {types}); the field passes through uncoerced.
  • data-router:function-key-namespace:* addSource("{id}") sets `key` but the rowKey function does not read exactly one row property, so its ids cannot be namespaced and rows from different feeds sharing an id will replace one another. Use a property-name rowKey, or give each feed ids that are already distinct.
  • data-router:join-aggregate-bad-fn join aggregate "{name}" uses fn "{fn}", which is not count|sum|avg|min|max|distinctCount|first|last; the aggregate is skipped.
  • data-router:join-aggregate-bad-spec join aggregate "{name}" is not a { fn, field? } reducer; the aggregate is skipped.
  • data-router:join-aggregate-invalid a join aggregate must be a non-empty object of named reducers; ignoring the join.
  • data-router:join-aggregate-no-field join aggregate "{name}" ({fn}) needs a field; the aggregate is skipped.
  • data-router:join-bad-missing join.missing "{value}" is not hold|passthrough|null; using "passthrough".
  • data-router:join-no-as a collect join (many: true) needs an `as` (the field to put the collected array into); ignoring the join.
  • data-router:join-no-fields join has no `fields`/`select`; rows pass through unenriched.
  • data-router:join-no-foreignkey join with a fn `localKey` needs an explicit string/fn `foreignKey`; ignoring the join.
  • data-router:join-no-from join needs a `from` (the lookup source id); ignoring the join.
  • data-router:join-no-localkey join needs a `localKey` (a field name or fn); ignoring the join.
  • data-router:link-predicate-unsupported link()/relate() source grid has every row matching a filter selected on a source that holds only the rows it has loaded. The relation does not declare it accepts a predicate selection - a function taking (selected, { predicate, count }) as its second parameter - so the link cannot build a target-row predicate from a truncated key list and imposes no constraint.
  • data-router:mutual-fn relate({ mutual: true }) with a function relation cannot be inverted; the same predicate is applied in both directions. Use a { from, to } key map for a mutual edge.
  • data-router:no-broadcastchannel router.broadcast() needs the BroadcastChannel API, which this runtime does not provide; cross-tab mirroring is off.
  • data-router:no-buffer {method}() needs a buffered window; call router.buffer({ window, max }) first to record one.
  • data-router:no-indexeddb router.persist() found no usable IndexedDB (a non-browser runtime, private mode, or blocked storage); the router runs in-memory with no durable resume. Pass { storage } to use another backend.
  • data-router:no-onwrite a route attached with { writable: true } has no onWrite handler (per-route or router-global); the edit was left in place but not persisted. Pass onWrite to route the write.
  • data-router:overlap:* Data Router: value {value} already routes to another viewer; with overlap:false only the first receives rows. Pass createDataRouter({ overlap: true }) to fan one value to several viewers.
  • data-router:readonly-write an edit on a derived (rollup/transform) route is read-only and was reverted; a derived row cannot be inverted to a source row. Make the source route writable instead.
  • data-router:relaterows-missing-source relateRows needs both `child` and `parent` to be registered source ids (or the handles addSource returned); the rule was ignored.
  • data-router:relaterows-no-foreignkey relateRows needs a `foreignKey` (the child field holding the parent key); the rule was ignored.
  • data-router:remove-rejected:* route "{name}" asked its target to remove {n} row(s) the target does not have (for example "{id}"). The rows are still on screen. This is a key mismatch: the key the route derives for a row is not the key the grid derives for it. Check that the route's `rowKey` names the same value as the grid's.
  • data-router:spread-no-name a spread join needs a `name` (the attribute-name field or fn); ignoring the join.
  • data-router:spread-no-value a spread join needs a `value` (the attribute-value field or fn); ignoring the join.
  • data-router:unnest-no-as an unnest needs an `as` (the child row type); ignoring the unnest.
  • data-router:unnest-no-path an unnest needs a `path` (the dotted array field on the row); ignoring the unnest.
  • data-router:writable-target attach({ writable: true }) needs a grid exposing grid.on and grid.edit.setCells; write-back is off for this route.
  • router:coerce-failed:*:* source "{sourceId}" field "{field}": could not coerce {example} to {type}; the value is stored as null.
  • router:join-cycle join from "{from}" to "{to}" would close a join cycle; the join is refused.
  • router:orphaned-rows {count} child row(s) were orphaned when their parent was deleted; they are kept and marked __orphan: true (onParentDelete: 'orphan').
  • router:spread-collision a spread join skipped the field "{fieldName}" because the parent row already carries it; a spread field never overwrites a base parent field.
  • router:spread-duplicate a spread join wrote the field "{fieldName}" more than once for one parent; the last attribute row (source order) wins.
  • router:unnest-cycle unnest "{from}" → "{to}" would close an unnest cycle; the unnest is refused.
  • router:unnest-duplicate-key the unnest "{as}" produced the child key "{key}" more than once for one parent; the later child (source order) wins.

designer (35)

  • designer:addPanel designer addPanel: there is no page with a spec to add to; nothing was added.
  • designer:assist designer.assist: there is no AI on this designer (options.llm); nothing was asked. Pass llm as the same ask() createAI takes, or as an object with ask and sendSampleRows to opt into sample rows.
  • designer:assist:accept designer.assist.accept: there is no page and no proposal to accept; nothing was applied.
  • designer:assist:ai designer.assist: the guardrails disable the AI assistant (guardrails.ai: false); nothing was asked.
  • designer:assist:reply designer.assist: the model's reply was not a proposal; nothing was shown. Return a JSON object with a panels array, or an object whose structured, spec or text carries that JSON.
  • designer:assist:source designer.assist: there is no source the guardrails allow with any fields; nothing was asked.
  • designer:command designer.commands.register: a command is a string name and a function; nothing was registered.
  • designer:command:* designer.commands.run: there is no command {name}; nothing was run.
  • designer:config-router:connect designer.configRouter.connect: the first argument is not a Data Router (no subscribe); nothing was connected.
  • designer:derived:from designer.addDerivedGrid: `from` must be a source or panel id; nothing was added.
  • designer:derived:pushdown:*:* designer.addDerivedStep: the {kind} step is refused: {reason}; nothing was added.
  • designer:derived:step:* designer.addDerivedStep: {step} is not a filter, group, pivot, join, sort or topN step; nothing was added.
  • designer:drop-field:*:* designer: there is no visible field {field} on source {source}; nothing was added.
  • designer:guardrails designer.setGuardrails: the guardrails are not an object ({type}); the guardrails were kept.
  • designer:mode createDesigner: mode {mode} is not edit or view; the designer opened in view mode.
  • designer:page:* designer.selectPage: there is no page {id}; the selection was kept.
  • designer:page:add designer.addPage: adding a page is not allowed by guardrails.pages.add; nothing was added.
  • designer:page:remove designer.removePage: removing a page is not allowed by guardrails.pages.remove; nothing was removed.
  • designer:page:rename designer.renamePage: renaming a page is not allowed by guardrails.pages.rename; nothing was changed.
  • designer:page:reorder designer.movePage: reordering a page is not allowed by guardrails.pages.reorder; nothing was moved.
  • designer:property:* designer.getProperty/setProperty: there is no property {name}; nothing was read or changed. The properties are mode, state, selectedPageId, guardrails.
  • designer:region designer.regions.register: a region is a string name and an element; nothing was registered.
  • designer:relationship:*:* designer: a relationship names "source.field" on both `from` and `to`; {from} -> {to} was ignored.
  • designer:route:no-adapter designer: issueQuery needs an adapter with an execute(query) function (or executeGroupedAggregates for a grouped route); nothing was asked.
  • designer:route:refused:* designer: the routed widget {id} is not allowed here, so it was refused - the same answer the {reason} guardrail gives an authored widget.
  • designer:route:unreachable:*:* designer: the widget {id}'s {role} role binds {field}, and no declared relationship reaches {source} from {from}; the field was left out of its route.
  • designer:set-mode designer.setMode: {mode} is not edit or view; the mode was kept.
  • designer:shortcut designer.shortcuts.register: a shortcut is { id, keys, description, run? } with string id, keys and description; nothing was registered.
  • designer:sources:set designer.setSources: the sources are not an object ({type}); the sources were kept.
  • designer:sources:update:* designer.sources.update: there is no declared source {id}; nothing was changed.
  • designer:sources:update:change designer.sources.update: the change is neither { rows } nor { apply: { add, update, remove } }; nothing was changed.
  • designer:sources:update:kind:* designer.sources.update: source {id} is {kind}, not 'rows'; its rows cannot be patched. Use setSources to replace its definition instead.
  • designer:state designer.setState: the state is not an object ({type}); the state was kept.
  • designer:state:newer designer: the state's schemaVersion {from} is newer than this designer's {version}; the state was refused, nothing was loaded.
  • designer:theme designer theme: {value} is not a theme name such as light, dark, auto or high-contrast; the theme was kept.

Kanban (28)

  • kanban:addRow the bound grid cannot append a row (no mutate.append).
  • kanban:apply-gridbound rows.apply on a grid-bound board is ignored; route to the bound grid instead.
  • kanban:blocked-move:* a move of blocked card "{card}" into "{column}" was refused: a blocked card cannot move forward.
  • kanban:blocked-writeback-noop blocking/unblocking did not persist through the bound grid: writing "{fieldPath}" was refused (a read-only, non-editable, permission-blocked or unresolvable column).
  • kanban:children-factory a card pop-out needs children.factory (a createGrid), children.render, or children.asBoard.
  • kanban:columnProperty a board needs a "columnProperty" to group cards into columns.
  • kanban:event:* "{name}" is not a board event; expected one of {list}.
  • kanban:flow-chart-deps flow.renderChart needs createChart and createGrid handed in (e.g. from 'lattice-grid/modules/charts' and 'lattice-grid').
  • kanban:flow-chart-kind:* "{kind}" is not a flow chart; expected cfd, wip, throughput, cycleTime or leadTime.
  • kanban:redo:vetoed a redo was refused by a before-event handler or the write-back path; the card and history are unchanged.
  • kanban:setRows-gridbound setRows on a grid-bound board is ignored; the rows come from the bound grid.
  • kanban:sla:no-threshold sla is enabled but no warn/breach threshold is set (globally, per column or per lane); no card will ever age.
  • kanban:source:aggregates the pushdown source routes the board's column count/sum aggregates client-side (its `aggregates` config defaults to client), so column totals cannot come from the engine. Create the source with `aggregates: { default: 'engine' }` for engine totals.
  • kanban:source:page the pushdown source failed to answer a column page: {error}.
  • kanban:source:rowkey a row loaded from the pushdown source has no row key and was left out; check the board `rowKey` against the engine schema.
  • kanban:source:totals the pushdown source could not compute column totals in the engine (the no-mixed-provenance guard fell every aggregate client-side), so column counts and sums are unavailable rather than computed over the loaded page.
  • kanban:source:totals:error the pushdown source failed to answer column totals: {error}.
  • kanban:undo:vetoed an undo was refused by a before-event handler or the write-back path; the card and history are unchanged.
  • kanban:unplaced:* {n} card(s) have a "{columnProperty}" value outside the configured columns and are not shown; add the column or map the value.
  • kanban:wip:* a move into "{column}" was refused: it is at its WIP limit of {limit}.
  • kanban:writeback-noop an inline edit to "{field}" did not persist through the bound grid (a read-only, non-editable, permission-blocked or unresolvable column).
  • kanban:writeback-source a card move did not persist: the bound pushdown source has no `edit.commit` (build it with `createPushdownSource({ edit: true })` over an adapter that declares `mutate.update`), so the card stayed where it was.
  • kanban.filters.engine.fn:* board.filters.engine("{name}", condition) needs a condition tree; the filter was not registered. Pass null to remove a registered filter.
  • kanban.filters.engine.name board.filters.engine(name, condition) needs a non-empty string name; the filter was not registered.
  • kanban.filters.engine.unsourced an engine filter was registered on a board with no pushdown source: the condition tree is stored but does not narrow cards - bind a `source` (and use `board.filters.where` for client-side predicates).
  • kanban.filters.reapply:* board.filters.reapply({name}) names no registered predicate; nothing was re-run.
  • kanban.filters.where.fn:* board.filters.where("{name}", fn) needs a function; the predicate was not registered. Pass null to remove a registered predicate.
  • kanban.filters.where.name board.filters.where(name, fn) needs a non-empty string name; the predicate was not registered.

Gantt (26)

  • gantt-cycle-* gantt: dependency cycle detected ({path}); schedule refused.
  • gantt-delete-unknown-* gantt: deleteTask for unknown task "{id}" ignored.
  • gantt-destroyed gantt: {method} on a destroyed controller ignored.
  • gantt-edit-loadonly-* gantt: applyEdit on load-only task "{id}" ignored; load-only bookings are not editable.
  • gantt-edit-mapped-field gantt: this plan reads {fields} through a `fields` FUNCTION, which has no inverse, so applyEdit writes the canonical property name and the edit will not be read back. Map those fields to a field NAME instead, or edit the host row through your own writer and feed the result in with rows.apply.
  • gantt-edit-unknown-* gantt: applyEdit for unknown task "{id}" ignored.
  • gantt-histogram-squeeze gantt: the histogram band needs the split view at least {needed}px tall to show its bars alongside the workload band (it is {height}px); showing its header only until there is room.
  • gantt-level-mapped-start gantt: level() is not available on a plan that maps `fields.start`, because leveling has to write a new start back and a field mapping only says how to READ one. Level the plan with its own `start` field, or apply the returned moves yourself.
  • gantt-level-unresolved gantt: resource leveling hit its {n}-iteration cap with {m} over-allocation(s) unresolved; returning the partial result.
  • gantt-link-shorthand-*-* gantt: dependency type {type} carries a lag of {lag} but lag: {explicit} was also given; the explicit lag wins. Give one or the other.
  • gantt-loadonly-dependency gantt: a dependency naming a load-only task is ignored; load-only bookings take no part in scheduling.
  • gantt-move-cycle-* gantt: moveRow refused - "{newParent}" is "{id}"'s own descendant.
  • gantt-move-loadonly-* gantt: moveRow on load-only task "{id}" ignored; load-only bookings are not editable.
  • gantt-move-unknown-* gantt: moveRow for unknown task "{id}" ignored.
  • gantt-move-unknown-parent-* gantt: moveRow for "{id}" names unknown parent "{newParent}", ignored.
  • gantt-mspdi-not-project gantt: importMSPDI input is not an MSPDI <Project> document.
  • gantt-print-no-split gantt: print() needs a mounted split view (mountSplit); the plain view has no table or bands to print.
  • gantt-print-not-drawn gantt: print() called before anything was drawn; nothing to print.
  • gantt-state-no-container gantt: setState was asked to show a view but no container is known yet; call mount()/mountSplit() at least once first.
  • gantt-state-version gantt: setState received a state from a newer version ({got} > {known}); unrecognised fields are ignored.
  • gantt-summary-link-*-*-* gantt: {type} link with a summary endpoint ({from} -> {to}) is expanded to descendant leaves - a conservative v1 reading; only FS is exact for summary endpoints.
  • gantt.marker.* gantt: marker {i} has no drawable date or from/to range; it is skipped.
  • gantt.projectEpoch:* gantt: projectEpoch {value} is not a date or a day number; the plan is rendered against the Unix epoch (day 0 = 1970-01-01).
  • gantt.today:* gantt: today {value} is not a date or a day number; no today line is drawn.
  • gantt.workloadExternal.* gantt: workload.external entry {i} has no resource or no drawable start/end; it is skipped.
  • gantt.zoomWidth:* gantt: mount was given both zoom ({zoom}) and width ({width}); zoom fixes the pixels-per-day, so the width is ignored and the plot scrolls. Drop one of the two.

KPI (20)

  • kpi:age-clock:* config.ageBy {named} read no time from any of the panel's {n} rows, so the panel cannot tell a silent feed from a live one and grades every tile as usual - check it against the rows (or the bound grid's column ids), or drop `ageBy` to measure arrival time instead.
  • kpi:ageBy-no-maxAge config.ageBy names the clock `maxAge` reads, and this panel sets no `maxAge`, so nothing reads it - add `maxAge` (ms) to say how long the panel waits before it stops grading what it holds.
  • kpi:agg:* "{aggregation}" is not a KPI aggregation; expected one of {list}. Falling back to count.
  • kpi:aggregate-unpushable:* the "{label}" tile's {aggregation} cannot be answered from the rows the bound grid has loaded, because {reason}. Unlike a sum or an average, a page's distinct count is not a partial reading of the relation's, so the tile shows no number rather than one that looks real and is not.
  • kpi:apply-gridbound rows.apply on a grid-bound KPI panel is ignored: the panel follows the bound grid, so route the change to the grid instead and the panel will follow it.
  • kpi:clock:ignored:*:* the "{label}" clock tile does not take `{key}`; a clock tile shows the device clock and `{key}` is ignored.
  • kpi:clock:tz:* the "{label}" clock tile's timeZone `{zone}` is not a recognised IANA zone; falling back to the local time zone.
  • kpi:delta:* kpi: tile "{id}" has delta: {value}; expected one of {list}. Falling back to 'both'.
  • kpi:engine-page:* the "{label}" tile reduces only the rows the bound grid has loaded, a window of the matching set, because {reason}. Its {aggregation} describes that window, not every matching row.
  • kpi:event:* "{name}" is not a KPI event; expected one of {list}.
  • kpi:fields:type config.fields must be an array of column names; ignoring it.
  • kpi:grid-destroyed this KPI panel was bound to a grid that has been destroyed, so it shows nothing. Point it at a replacement with kpi.rebind(grid).
  • kpi:no-column::* config.fields names `{field}`, which is not a column on the bound grid - check it against the grid's column ids.
  • kpi:no-column:*:* the "{label}" tile's field `{field}` names no column on the bound grid, so the tile has nothing to measure - check it against the grid's column ids.
  • kpi:pivot-unsupported:* the "{label}" tile cannot be computed over a pivoted pushdown grid because {reason}; a pivot keeps no leaf rows client-side, so it reports no data rather than a leaf-window figure.
  • kpi:requires-core the KPI module needs the Lattice core; load lattice-grid (or its script build) before modules/kpi.
  • kpi:setRows-gridbound setRows on a grid-bound KPI panel is ignored; the rows come from the bound grid.
  • kpi:tree:dotted-id kpi: tile ids contain dots; set `tree` to render them as a hierarchy.
  • kpi:tree:unplaced {n} KPI tile(s) are not on the tree and will not render: {ids}. Two tiles cannot share one path; a path names the tile's own place, last segment included.
  • kpi:unprojected:*:* the "{label}" tile's filter read `{field}`, which is a column on the bound grid but is not projected - add it to `fields`: createKPI({ grid, fields: ['{field}'], … }).

mapview (18)

  • mapview:buildings createMapView: buildings is on but the basemap style has no vector source with an OpenMapTiles "building" layer; no buildings were drawn. Use an OpenMapTiles style such as the default.
  • mapview:color createMapView: color is neither a colour nor { field, map } nor { field, scale: 'ramp' }; every row is drawn in the theme's colour.
  • mapview:container createMapView: the map has nowhere to go: {reason}. Nothing was drawn.
  • mapview:dash createMapView: dash is set but deck.gl has no PathStyleExtension (the page's deck.gl build does not export it); the lines are drawn solid. Load deck.gl's full bundle, or pass it as options.libs.deck.
  • mapview:dash:spec createMapView: dash {value} is not a line style; the lines are drawn solid. Use "dashed", "dotted", [dash, gap] in pixels, or { field, map } naming one of those per value.
  • mapview:event:* createMapView: "{name}" is not an event a map view emits; use {events}.
  • mapview:geometry createMapView: the grid has no place to draw rows at: {reason}. Nothing was drawn.
  • mapview:grid createMapView: the first argument is not a grid; pass the grid whose rows the map shows. Nothing was drawn.
  • mapview:icon createMapView: icon is neither an icon name nor { field, map, default, size, anchor }; points are drawn as circles.
  • mapview:icon:* createMapView: icon "{name}" is not a glyph of the grid's icon registry, an SVG path or an image URL; the points that ask for it are drawn as circles.
  • mapview:icons createMapView: icon is set but there is no canvas to draw the icon atlas on; points are drawn as circles.
  • mapview:layer:* createMapView: layer "{layer}" is not a layer kind; each row is drawn by the default for its geometry. Use "point", "column", "polygon" or "path".
  • mapview:libs createMapView: {library} {is/are} not on the page; load the UMD build (it sets the global) or pass options.libs = { deck, maplibregl }. The map was left empty.
  • mapview:listener:* createMapView: a "{name}" listener threw: {error}
  • mapview:over createMapView: the map's view holds {count} matching rows, more than its viewportCap of {cap}, and {reason}, so none were drawn. Zoom in, narrow the filter, or raise viewportCap.
  • mapview:requires-core the map view module needs the Lattice core; load lattice-grid (or its script build) before modules/mapview.
  • mapview:route createMapView: route.aggregate {value} is not an aggregate; a route's style is read from its first row. Use {aggregates}.
  • mapview:selection createMapView: row {key} was clicked on the map but the grid did not select it: a grid selects nothing unless it is created with selection: 'single' or 'multiple', and a beforeSelect handler can refuse it.

Layout (15)

  • layout.close.unknown layout: close("{id}") - no such window.
  • layout.compact.unknown layout: compact: {value} is not a compaction mode; expected {modes}. Falling back to "vertical".
  • layout.corners.unknown layout: corners: {value} is not a corner style; expected {styles}. Falling back to "rounded".
  • layout.length:* layout: {setting} was set to {value}, which is not a length this module understands (a number of pixels, or a string like '200px', '25%', '1fr', '2rem'). Using {fallback}.
  • layout.maximise.unknown layout: maximise("{id}") - no such window.
  • layout.minimise.chrome.* layout: minimise("{id}") - a window with chrome: false cannot be minimised; there would be nothing left on screen to restore it with.
  • layout.minimise.unknown layout: minimise("{id}") - no such window.
  • layout.move.mode.* layout: move("{id}") - the window is maximised or minimised and owns its own geometry; restore() it first.
  • layout.move.unknown layout: move("{id}") - no such window.
  • layout.overlap.repaired layout: overlapping windows {ids} were moved to the next free cells to keep the layout valid.
  • layout.restore.unknown layout: restore("{id}") - no such window.
  • layout.select.unknown.* layout: select("{id}") - no such window; the selection was kept.
  • layout.setInteractive.* layout: setInteractive({{capability}}) takes a boolean; {value} was ignored.
  • layout.setInteractive.type layout: setInteractive() takes a boolean, or an object of {movable, resizable, closable}; {type} was ignored.
  • layout.theme.type layout: theme must be a theme name such as 'dark', 'light' or 'auto'; got {theme}, so the layout follows the page.

Tabs (15)

  • tabs.activate.unknown tabs: activate("{id}") - no such tab.
  • tabs.badge.build:* tabs: tab "{id}" - its headless count grid could not be built ({error}); no badge is shown for it.
  • tabs.badge.fn:* tabs: tab "{id}" - its badge function threw ({error}); no badge is shown.
  • tabs.badge.headless tabs: a tab configured with `badge` has never been activated, so it has no grid to count and shows no badge until it is. Pass `config.createHeadlessGrid` - e.g. `import { createHeadlessGrid } from 'lattice-grid'` - to give every tab a live count from first paint.
  • tabs.badge.type:* tabs: tab "{id}" - badge must be true, a number, a string, or a function; got {type}. No badge is shown for this tab.
  • tabs.badgeTone.fn:* tabs: tab "{id}" - its badgeTone function threw ({error}); the badge is drawn untoned.
  • tabs.badgeTone.type:* tabs: tab "{id}" - badgeTone must be one of {tones}, or a function returning one; got {value}. The badge is drawn untoned.
  • tabs.badgeTone.value:* tabs: tab "{id}" - badgeTone returned "{value}", which is not one of {tones}. The badge is drawn untoned.
  • tabs.createHeadlessGrid.type tabs: config.createHeadlessGrid must be the headless grid factory (a function); got {type}. It is ignored, so a tab that has never been activated shows no badge until it is.
  • tabs.icon:* tabs: tab "{id}" - icon must be a single character or emoji, or an Element; got {got}. It is ignored. Markup is never parsed here: build the element and pass it instead.
  • tabs.on.unknown:* tabs: "{name}" is not an event this module emits.
  • tabs.source.overridden tab "{id}": config.source is replaced by the derived source built from from: "{parent}" - set the narrowing (where/group/join/…) on the tab descriptor itself rather than on config.source.
  • tabs.view.build:* tabs: tab "{id}" - the headless grid deriving its rows could not be built ({error}); the body mounts with its own config's rows and does not follow.
  • tabs.view.headless tabs: tab "{id}" derives from "{parent}" and its body is not a grid, so its rows have to be derived by a headless grid. Pass `config.createHeadlessGrid` - e.g. `import { createHeadlessGrid } from 'lattice-grid'` - or the body mounts with whatever rows its own config carries and does not follow its parent.
  • tabs.view.type:* tabs: tab "{id}" - view must be a factory function, (el, config) => instance, such as createKanban or createKPI; got {type}. This tab is mounted as an ordinary grid.

dashboard (12)

  • dashboard:el createDashboard: the first argument is not an element to mount on. Nothing was built.
  • dashboard:layout createDashboard: there is no createLayout on the page; load modules/layout or pass options.createLayout. Nothing was built.
  • dashboard:link:*->* createDashboard: the link from "{from}" to "{to}" {what}
  • dashboard:panel:* createDashboard: panel "{id}" {what}
  • dashboard:present dashboard.present: {reason}; nothing is presented.
  • dashboard:proposal dashboard.propose: the proposal has problems, each in the returned warnings: {first}
  • dashboard:propose dashboard.propose: there is no AI on this dashboard (options.ai.ask); nothing was asked. Load modules/ai and pass the same ask() createAI takes.
  • dashboard:router dashboard.apply: {count} grid(s) read a Data Router route and were rebuilt; a router does not re-send rows to a route attached after they arrived, so they fill from its next load() or push().
  • dashboard:spec createDashboard: {reason}; a spec is { layout, panels, sources, links }. Nothing was built.
  • dashboard:spec:* createDashboard: spec {path} {problem}
  • dashboard:view dashboard.applyView: {view} is not a saved view; nothing was applied.
  • dashboard:view-name dashboard.saveView: {name} is not a view name; nothing was saved.

htmx (11)

  • htmx:designer-no-factory autoInitDesigner: no Designer factory registered and no globalThis.LatticeGridDesigner.createDesigner; load the Designer module first.
  • htmx:form-no-enhancer autoInitForm: no form enhancer registered and no globalThis.LatticeGridForm.enhanceForm; load the form module first.
  • htmx:gantt-no-factory autoInitGantt: no Gantt factory registered and no globalThis.LatticeGridGantt.createGantt; load the Gantt module first.
  • htmx:gantt-no-table autoInitGantt: data-lattice-gantt found no <table> to read; skipped.
  • htmx:history-snapshot-too-large htmx could not store this page's history snapshot (its sessionStorage quota was exceeded), so browser Back will re-fetch the page instead of restoring the grid; scope the hx-history-elt subtree smaller, or lower htmx's historyCacheSize.
  • htmx:kanban-no-factory autoInitKanban: no Kanban factory registered and no globalThis.LatticeGridKanban.createKanban; load the Kanban module first.
  • htmx:map-no-factory:* autoInitMap: no {kind} factory registered and no globalThis.{globalName}; load the map module first (or registerMapFactories).
  • htmx:map-no-grid autoInitMap: data-lattice-map found no live grid for its data-lattice-bind/config.grid; skipped.
  • htmx:scheduler-no-factory autoInitScheduler: no scheduler factory registered and no globalThis.LatticeGridScheduler.createScheduler; load the scheduler module first.
  • htmx:scheduler-no-table autoInitScheduler: data-lattice-scheduler found no <table> to read; skipped.
  • htmx:shell-no-factory autoInitShell: no shell factory registered and no globalThis.LatticeGridShell.createShell; load the shell module first.

AI (8)

  • ai:actorbar:no-el ai.actorBar() needs a DOM element to mount into; none was given and there is no document.
  • ai:askbar:no-el ai.askBar() needs a DOM element to mount into; none was given and there is no document.
  • ai:event:* "{name}" is not an AI event; expected one of {list}.
  • ai:explain:no-doc attachExplain() needs a document to build a button.
  • ai:insights:no-el ai.insights() needs a DOM element to mount into; none was given and there is no document.
  • ai:narrative:empty the AI ask() returned no narrative text; nothing to show. Ensure your ask() resolves to a string or { text } for a narrative prompt.
  • ai:no-grid createAI needs a Lattice grid instance to ground on (grid.statistics/grid.rows). Pass the grid returned by createGrid or createHeadlessGrid.
  • ai:risk:no-sources a risk summary needs a Gantt and/or a Kanban board to ground on. Pass { kind: "risk", gantt, board } (or precomputed earnedValue/schedule/breaches); there is nothing to summarise.

The grid (background worker) (8)

  • worker/blob-failed could not construct an inline worker; compute will run on the main thread
  • worker/error compute worker failed; falling back to the main thread
  • worker/kernel-missing (the worker's own error message, forwarded verbatim)
  • worker/local-* {op} received a function argument and cannot cross the worker boundary; running on the main thread
  • worker/no-blob no blob: URL support and no config.workerUrl; compute will run on the main thread
  • worker/no-compute compute kernels could not be loaded; sorting and filtering are unavailable
  • worker/ready-no-compute worker started without compute kernels; compute falls back to the main thread (columnization still offloads)
  • worker/shared-unavailable sharedMemory was requested but the page is not cross-origin isolated; falling back to the copying transport

calendar (6)

  • calendar:apply-gridbound rows.apply on a grid-bound calendar is ignored; route to the bound grid instead.
  • calendar:event:* "{name}" is not a calendar event; expected one of {events}.
  • calendar:setRows-gridbound setRows on a grid-bound calendar is ignored; the rows come from the bound grid.
  • calendar:state-version calendar: restoring state version {version} into a version-2 calendar; unknown fields are skipped.
  • calendar:unparsable-start calendar: a row's "{startProperty}" is empty or unparsable, so it is not shown; count it in `unscheduled`.
  • calendar:writeback-noop a card reschedule did not persist through the bound grid: writing "{startProperty}" was refused (a read-only, non-editable, permission-blocked, unresolvable or invalid column), so the card stayed where it was.

deckgl (6)

  • deckgl:deck bindDeck: options.deck has no setProps(); pass the page's deck.gl Deck instance. Nothing was bound.
  • deckgl:layers bindDeck: options.layers is not a function; pass (rows, ctx) => Layer[] built with your deck.gl classes. Nothing was bound.
  • deckgl:over bindDeck: deck's view holds {count} matching rows, more than its viewportCap of {cap}, and {reason}, so no rows were handed to the layers. Zoom in, narrow the filter, or raise viewportCap.
  • deckgl:position bindDeck: the grid has no position: {reason}. Nothing was bound.
  • deckgl:requires-core the deck.gl module needs the Lattice core; load lattice-grid (or its script build) before modules/deckgl.
  • deckgl:viewports bindDeck: viewportFilter is on but options.deck has no getViewports(), so its view cannot be read as a box; the grid is not filtered by it.

leaflet (6)

  • leaflet:layers bindLeaflet: options.layers is not a function; pass (rows, ctx) => L.Layer[] built with your Leaflet. Nothing was bound.
  • leaflet:map bindLeaflet: options.map has no {methods}(); pass the page's Leaflet map (L.map(...)). Nothing was bound.
  • leaflet:over bindLeaflet: the map's view holds {count} matching rows, more than its viewportCap of {cap}, and {reason}, so no rows were handed to the layers. Zoom in, narrow the filter, or raise viewportCap.
  • leaflet:position bindLeaflet: the grid has no position: {reason}. Nothing was bound.
  • leaflet:requires-core the Leaflet module needs the Lattice core; load lattice-grid (or its script build) before modules/leaflet.
  • leaflet:selection bindLeaflet: row {key} was selected from the map but the grid did not take it: a grid selects nothing unless it is created with selection: 'single' or 'multiple', and a beforeSelect handler can refuse it.

Shared across modules (5)

  • *.beforeReject:* {module}: a '{event}' before-handler rejected; the action was cancelled.
  • *.beforeThrow:*:* {module}: a '{event}' before-handler threw; the action was cancelled.
  • *.emit.throw:*:* {module}: a '{event}' handler threw; other handlers were unaffected.
  • designer.mountOnly.* {Designer}: `{props}` changed, but the designer takes {it|them} only when created - there is no live setter, and rebuilding it silently would throw away the author's undo history, selection and open panels. Nothing was applied and the designer was NOT rebuilt. Give the component a `key` that changes when this must take effect, so the rebuild is yours and visible, or drive the instance through the ref.
  • shared.autosize.no-resize-observer ResizeObserver is unavailable; this view was sized once at mount and will not follow its container.

Alarms (4)

  • alarms:grid-no-columns alarms.attach(grid) was given no `columns`, so no cell can raise anything. Declare the columns to watch: attach(grid, { columns: { cpu: { thresholds: { warn: 80, critical: 95, direction: 'lowerIsBetter' } } } }).
  • alarms:publish-no-router alarms.publish() needs a data router with addSource(); nothing was published.
  • alarms:unknown-event:* alarms.on('{name}') will never fire; an alarm set emits {events}.
  • alarms:unknown-source alarms.attach() was given something that is not a KPI panel, a grid or a data router; nothing was attached and no alarm can be raised from it.

dhtmlx-compat (4)

  • dhtmlx.*.dropped:* dhtmlx-compat does not translate the {kind} key '{key}', so it has no effect. Nothing was applied for it.
  • dhtmlx.editorType.unknown:* dhtmlx-compat does not recognise editorType '{editorType}'; the default text editor was used instead.
  • dhtmlx.header.multirow dhtmlx-compat collapsed a {n}-row header to its first row. Lattice titles are a single string; the remaining rows were not applied.
  • dhtmlx.span.dropped:* dhtmlx-compat does not translate the span key '{key}', so it has no effect. Lattice spans carry only geometry (rowspan/colspan); the cell's value, css and tooltip come from its own column, not the span.

geometry (3)

  • filter:geometry:* a "{op}" condition was refused: {reason}. It matches no row rather than guessing.
  • geometry:format:* column "{col}" asks for geometry format pattern "{pattern}", which is not one of {list}; the summary is shown instead.
  • geometry:refused:* column "{col}" refused a value that is not a geometry ({reason}); the cell is left blank rather than storing it. A geometry column accepts WKB, WKT or GeoJSON.

form (3)

list (3)

  • list:item:* column "{col}" refused the item {item}: it is not a value of the item type "{type}". The item is left out of the stored list rather than kept.
  • list:parse:* column "{col}" refused the item {item}: it is not a value of the item type "{type}". Nothing was stored.
  • list:scalar:* column "{col}" is a list column and received a single value ({kind}) where a list was expected; it is read as a one-item list. The row itself is not changed: pass an array to say so.

React adapter (3)

  • react:mui-prop-unsupported:* <LatticeDataGrid> ignored "{prop}": {reason} Nothing of it was applied.
  • react.tabs.mountOnly.tabs LatticeTabs: `tabs` changed (a label, a badge or a descriptor), but the strip takes it only when created - the module has no way to repaint an existing tab. Nothing was applied and the strip was NOT rebuilt - rebuilding it would lose whichever tab is open and any state its content holds. Give the component a `key` that changes when a genuine rebuild is wanted; a live badge otherwise needs to live in your own render, as `tab.badge` on this component's own props does today.
  • react.viewer.*.mountOnly.* {Viewer}: `{prop}` changed, but {Viewer} takes {it|them} only when the viewer is created. Nothing was applied and the viewer was NOT rebuilt - rebuilding it silently would throw away scroll position, selection and expansion. Give the component a `key` that changes when this must take effect, so the rebuild is yours and visible, or drive the instance through the ref.

scheduler (3)

Angular adapter (2)

  • angular.deprecated modules/angular is deprecated: it needs the JIT compiler. Use @toclocoinc/lattice-grid/angular.
  • angular.tabs.mountOnly.tabs <lattice-tabs>: `tabs` changed (a label, a badge or a descriptor), but the strip takes it only when created - the module has no way to repaint an existing tab. Nothing was applied and the strip was NOT rebuilt, because rebuilding it would lose whichever tab is open and any state its content holds. Put the strip inside an `@if` keyed on what changed if a genuine rebuild is wanted; a live badge otherwise belongs in your own template.

Svelte adapter (2)

  • svelte.grid.mountOnly.name LatticeGrid: `name` changed from "{was}" to "{now}", but a grid publishes itself under the name it had when it mounted. Nothing was republished and the grid was NOT rebuilt - rebuilding it would throw away scroll position, selection and expansion. Give the component a `key`-style `{#if}` when a genuine rebuild is wanted.
  • svelte.tabs.mountOnly.tabs LatticeTabs: `tabs` changed (a label, a badge or a descriptor), but the strip takes it only when created - the module has no way to repaint an existing tab. Nothing was applied and the strip was NOT rebuilt, which would have lost whichever tab is open and any state its content holds. Wrap the strip in an `{#if}` you toggle when a genuine rebuild is wanted; a live badge otherwise belongs in your own markup.

Web component (2)

  • webcomponent.router.mountOnly.config <lattice-router>: `config` changed after the router was built. Nothing was applied and the router was NOT rebuilt - rebuilding it would detach every grid and drop every row it holds. Replace the <lattice-router> element itself when a genuinely different router is wanted.
  • webcomponent.write:* <lattice-grid>: `{key}` could not be written to the grid (it has no API for it), so it had no effect.

presets (1)

  • presets:themefrommui-apply-no-element themeFromMui(theme).apply(element) was given something with no style.setProperty - pass the element you want themed, e.g. apply(document.querySelector(".lattice")).

Vue adapter (1)

  • vue.tabs.mountOnly.tabs LatticeTabs: `tabs` changed (a label, a badge or a descriptor), but the strip takes it only when created - the module has no way to repaint an existing tab. Nothing was applied and the strip was NOT rebuilt - rebuilding it would lose whichever tab is open and any state its content holds. Give the component a `key` that changes when a genuine rebuild is wanted; a live badge otherwise needs to live in your own render.