commaai/openpilot · error · ValueError

start index must be greater than zero

Error message

start index must be greater than zero

What it means

require_nal_unit_start() validates that dat[nal_unit_start:] begins with a NAL start code; it first requires the index itself to be >= 1 because a start code is at least one byte in and index 0 would be ambiguous with the reader's assumptions. Violating that precondition raises ValueError immediately.

Source

Thrown at openpilot/tools/lib/vidindex.py:152

      elif prefix_val == 0:
        prefix_val = (dat[i] >> j) & 1
        prefix_len += 1
      else:
        suffix_val = (suffix_val << 1) | ((dat[i] >> j) & 1)
        suffix_len += 1
      j -= 1

      if prefix_val == 1 and prefix_len - 1 == suffix_len:
        val = int(2**(prefix_len-1) - 1 + suffix_val)
        size = prefix_len + suffix_len
        return val, size
    i += 1

  raise VideoFileInvalid("invalid exponential-golomb code")

def require_nal_unit_start(dat: bytes, nal_unit_start: int) -> None:
  if nal_unit_start < 1:
    raise ValueError("start index must be greater than zero")

  if dat[nal_unit_start:nal_unit_start + NAL_UNIT_START_CODE_SIZE] != NAL_UNIT_START_CODE:
    raise VideoFileInvalid("data must begin with start code")

def get_hevc_nal_unit_length(dat: bytes, nal_unit_start: int) -> int:
  try:
    pos = dat.index(NAL_UNIT_START_CODE, nal_unit_start + NAL_UNIT_START_CODE_SIZE)
  except ValueError:
    pos = -1

  # length of NAL unit is byte count up to next NAL unit start index
  nal_unit_len = (pos if pos != -1 else len(dat)) - nal_unit_start
  if DEBUG:
    print("  nal_unit_len:", nal_unit_len)
  return nal_unit_len

def get_hevc_nal_unit_type(dat: bytes, nal_unit_start: int) -> HevcNalUnitType:
  # 7.3.1.2 NAL unit header syntax

View on GitHub (pinned to 516ec1e682)

Solutions

  1. Pass an index >= 1 pointing at a real start code (result of dat.find(b'\x00\x00\x00\x01', 1) or similar)
  2. Guard .find()/index() results: treat -1 and 0 as 'no start code here' before calling
  3. If your stream truly starts at byte 0, prepend nothing — reconsider: annex-B streams always have the start code at 0, so use a different entry point for that case

Example fix

# before
require_nal_unit_start(dat, dat.find(NAL_UNIT_START_CODE))  # -1 or 0 possible

# after
pos = dat.find(NAL_UNIT_START_CODE, 1)
if pos >= 1:
    require_nal_unit_start(dat, pos)
Defensive patterns

Strategy: validation

Validate before calling

NAL_UNIT_START_CODE = b'\x00\x00\x00\x01'
pos = dat.find(NAL_UNIT_START_CODE, 1)
if pos >= 1:
    require_nal_unit_start(dat, pos)

Type guard

def is_valid_nal_start_idx(dat: bytes, i: int) -> bool:
    return isinstance(i, int) and i >= 1 and dat[i:i+4] == b'\x00\x00\x00\x01'

Prevention

When it happens

Trigger: Calling require_nal_unit_start(dat, 0) or with a negative index. Internally, callers pass indices found via searching for start codes, so hitting it externally usually means passing a hand-computed offset of 0.

Common situations: Custom parsing code that assumes the first NAL starts at byte 0; off-by-one when scanning for 00 00 00 01 patterns; negative index from a failed .find() (returns -1).

Related errors


AI-assisted analysis of commaai/openpilot@516ec1e682 (2026-08-15). Data as JSON: /api/errors/63f52e79c44cdadc. Report an issue: GitHub.