microsoft/qlib · error · IndexError
{i}: start index is {storage_start_index}
Error message
{i}: start index is {storage_start_index} What it means
Raised by FileFeatureStorage.__getitem__ (qlib/data/storage/file_storage.py) when an integer index i is smaller than the storage's start_index. Bin files only cover rows [start_index, end_index]; querying an index before the first stored row cannot be answered, so an IndexError names the requested index and the actual start index. Slices, by contrast, are clamped to the available range and never raise.
Source
Thrown at qlib/data/storage/file_storage.py:360
return None
# The next data appending index point will be `end_index + 1`
return self.start_index + len(self) - 1
def __getitem__(self, i: Union[int, slice]) -> Union[Tuple[int, float], pd.Series]:
if not self.uri.exists():
if isinstance(i, int):
return None, None
elif isinstance(i, slice):
return pd.Series(dtype=np.float32)
else:
raise TypeError(f"type(i) = {type(i)}")
storage_start_index = self.start_index
storage_end_index = self.end_index
with self.uri.open("rb") as fp:
if isinstance(i, int):
if storage_start_index > i:
raise IndexError(f"{i}: start index is {storage_start_index}")
fp.seek(4 * (i - storage_start_index) + 4)
return i, struct.unpack("f", fp.read(4))[0]
elif isinstance(i, slice):
start_index = storage_start_index if i.start is None else i.start
end_index = storage_end_index if i.stop is None else i.stop - 1
si = max(start_index, storage_start_index)
if si > end_index:
return pd.Series(dtype=np.float32)
fp.seek(4 * (si - storage_start_index) + 4)
# read n bytes
count = end_index - si + 1
data = np.frombuffer(fp.read(4 * count), dtype="<f")
return pd.Series(data, index=pd.RangeIndex(si, si + len(data)))
else:
raise TypeError(f"type(i) = {type(i)}")
def __len__(self) -> int:
self.check()View on GitHub (pinned to 79633dd950)
Solutions
- Query per-instrument ranges: use storage.start_index / storage.end_index to clamp, or use slice-based access which clamps automatically.
- Prefer qlib's expression/data APIs (D.features, Cal.calendar) over raw storage indexing; they handle per-instrument coverage.
- If the data should exist that far back, re-dump the instrument's bin file with a longer history.
Example fix
# before first = feature_storage[calendar_index] # calendar_index < storage.start_index -> IndexError # after start = feature_storage.start_index first = feature_storage[max(calendar_index, start)] # or simply use a slice, which clamps: first = feature_storage[calendar_index:calendar_index + 1]
Defensive patterns
Strategy: validation
Validate before calling
i = max(int(i), storage.start_index)
if i > storage.end_index:
i = storage.start_index # or handle 'no data' explicitly Type guard
def index_within_storage(i: int, storage) -> bool:
return storage.start_index <= i <= storage.end_index Try / catch
try:
_, val = storage[i]
except IndexError as e:
if "start index is" in str(e):
_, val = storage[storage.start_index] # clamp to first available row
else:
raise Prevention
- Always clamp indices against storage.start_index/end_index before int access.
- Prefer slice access, which clamps automatically, or qlib's D.features API.
When it happens
Trigger: feature_storage[1234] when that instrument's bin file starts at start_index=1500 (e.g. the stock listed later than the requested calendar position). Common when computing calendar-relative indices for an instrument whose data begins mid-calendar.
Common situations: Hand-computing absolute calendar indices and applying them to every instrument equally; early-listed vs late-listed instruments in a cross-section; code that assumes all bin files start at the same index.
Related errors
- type(i) = {type(i)}
- I forget why would this case appear. But I think it does not
- stock selector must be type str|list, or slice(None), rather
- {self.storage_name}: {self.provider_uri} does not contain da
- {self.storage_name} not exists: {self.uri}
AI-assisted analysis of microsoft/qlib@79633dd950 (2026-08-15).
Data as JSON: /api/errors/39d135d125a90dc4.
Report an issue: GitHub.