Skip to content

Fix IndexError in check_table_values_for_dict on empty tables (fixes #712)#715

Open
Leonard013 wants to merge 1 commit into
NeurodataWithoutBorders:devfrom
Leonard013:fix/712-empty-table-indexerror
Open

Fix IndexError in check_table_values_for_dict on empty tables (fixes #712)#715
Leonard013 wants to merge 1 commit into
NeurodataWithoutBorders:devfrom
Leonard013:fix/712-empty-table-indexerror

Conversation

@Leonard013

@Leonard013 Leonard013 commented Jul 12, 2026

Copy link
Copy Markdown

Motivation

check_table_values_for_dict crashes with IndexError: list index out of range
whenever it is run on a table that has a column with zero rows. Empty DynamicTable /
EventsTable objects are legitimate NWB constructs, so nwbinspector currently crashes
on any file that contains one.

Root cause (diagnosed by @oruebel in #712), in src/nwbinspector/checks/_tables.py,
the per-column skip condition:

for column in table.columns:
    if not hasattr(column, "data") or isinstance(column, VectorIndex) or not isinstance(column.data[0], str):
        continue

column.data[0] is evaluated with no empty-length guard, so a zero-length column
raises IndexError.

What

Add a len(column.data) == 0 guard to the skip condition, placed before the
column.data[0] access so Python's or short-circuits on empty columns:

for column in table.columns:
    if (
        not hasattr(column, "data")
        or isinstance(column, VectorIndex)
        or len(column.data) == 0
        or not isinstance(column.data[0], str)
    ):
        continue

The multi-line form matches the sibling check check_col_not_nan directly below in the
same file. black --line-length 120 and ruff both pass.

Why this does not over-skip non-empty columns

or evaluates left-to-right and stops at the first truthy operand:

  • Empty column (len(column.data) == 0 is True): short-circuits and continues,
    so column.data[0] is never evaluated — the crash is avoided.
  • Non-empty column (len(column.data) == 0 is False): evaluation falls through to
    not isinstance(column.data[0], str), exactly as before. The new term is False for
    every non-empty column, so it changes nothing about which non-empty columns get
    scanned, and the dict-detection logic is untouched.

Only genuinely empty columns are skipped.

Tests

Added test_check_table_values_for_dict_empty_column to tests/unit_tests/test_tables.py:

def test_check_table_values_for_dict_empty_column():
    table = DynamicTable(name="test_table", description="")
    table.add_column(name="test_column", description="")
    assert check_table_values_for_dict(table=table) is None
  • Before the fix: fails with IndexError at the diagnosed line.
  • After the fix: passes.

The existing test_check_table_values_for_dict_*_fail tests continue to pass, confirming
a non-empty column containing a dict-string still triggers the check (no over-skip).
pytest tests/unit_tests/test_tables.py → 48 passed; broad slice → 340 passed, 7 skipped.

Follow-up (out of scope for #712)

Scoped to check_table_values_for_dict per @oruebel's diagnosis. Three sibling checks in
the same file carry the same latent empty-table IndexError and would still crash on a
full run over an empty table — worth a separate follow-up PR if desired:

  • check_column_binary_capabilitycolumn.data[0]
  • check_col_not_nancolumn[0]
  • check_table_time_columns_are_not_negativetable[column_name][0]

Happy to open that follow-up.

CHANGELOG

Added an entry under # v0.7.3 (Upcoming)### Fixes.

An empty DynamicTable/EventsTable can contain columns whose data is
zero-length. check_table_values_for_dict evaluated `column.data[0]` in
its column-skip condition with no empty-length guard, so any NWB file
containing an empty table crashed nwbinspector with IndexError
(issue NeurodataWithoutBorders#712, diagnosed by @oruebel).

Add `len(column.data) == 0` to the skip condition, ordered so Python's
`or` short-circuits before `column.data[0]` is accessed. Only empty
columns are skipped; non-empty columns are scanned exactly as before.

Add a regression test that an empty column no longer raises; the
existing dict-string tests continue to confirm that non-empty columns
are still checked (the guard does not over-skip).

This change was written with the assistance of Claude (AI).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NX8ygWTRa86kE8k8Yn9bpu
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant