Skip to content

docs: add exception reference and error handling guide - #654

Merged
vishal-bala merged 2 commits into
redis:mainfrom
TheSaiEaranti:docs-exception-reference
Aug 7, 2026
Merged

docs: add exception reference and error handling guide#654
vishal-bala merged 2 commits into
redis:mainfrom
TheSaiEaranti:docs-exception-reference

Conversation

@TheSaiEaranti

@TheSaiEaranti TheSaiEaranti commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Fixes #496

Adds docs/api/exceptions.rst and links it from the API toctree, so the exception hierarchy has one discoverable home.

What's on the page

  • The hierarchy — all five classes in redisvl.exceptions, and a note that redis-py exceptions such as redis.exceptions.ConnectionError are not part of it. RedisVL wraps those and chains the original with raise ... from e, so __cause__ still holds it. That seemed like the detail most likely to trip someone writing an except clause.
  • A table mapping each exception to the operations that raise it, e.g. SchemaValidationErrorload(), RedisSearchErrorcreate() / delete() / search() / aggregate().
  • Four try/except examples, including the two the issue asks for.
  • Autodoc reference for each class, following the :members: / :show-inheritance: pattern used in schema.rst.

Examples are drawn from real raise sites

Rather than writing plausible-looking snippets, I traced each exception to where it is actually raised so the examples match current behaviour:

Exception Verified raise site What the example shows
SchemaValidationError storage.py _preprocess_and_validate_objects validate_on_load=True, since validation is off by default and the error cannot otherwise occur
QueryValidationError index.py _validate_query ef_runtime on a flat vector field — the concrete case that method rejects
RedisSearchError create, delete, search, aggregate wrapping a Redis-side failure
RedisModuleVersionError _check_svs_support, reached from create() ordering except clauses, since it subclasses RedisVLError and not RedisSearchError

The from_yaml(..., validate_on_load=True) form matches the example already in the SearchIndex docstring.

Definition of Done

  • Exception docs present and discoverabledocs/_build/html/api/exceptions.html renders, all five classes appear with their docstrings, RedisModuleVersionError.for_svs_vamana is documented, and the page is linked from the API index as "Exceptions".
  • make docs-build passes — exit 0. The build reports 11 warnings; all 11 are pre-existing and point at query.py / aggregate.py docstrings inherited from redis-py, index.md heading levels, and the MCP how-to cross-references. None reference exceptions.rst.
  • Examples align with current exception classes — per the table above.

codespell is clean on both files. No Python changed, so no behaviour change.

Notes

  • Docs-only, so this needs the auto:documentation label per CONTRIBUTING. I can't set labels as an outside contributor — could a maintainer add it?
  • I deliberately left uv.lock out. Running make docs-build with a recent uv rewrites it to lockfile revision 3 (a ~250 line diff) which has nothing to do with this change.
  • If docs: migrate from Sphinx to mkdocs material #608 (Sphinx → mkdocs) lands first, this page is a single autodoc file and cheap to port; happy to do that conversion if you'd prefer to wait for it.

Note

Low Risk
Documentation-only change with no runtime or API behavior impact.

Overview
Adds a dedicated Exceptions API page and links it from the API toctree so redisvl.exceptions is easy to find.

The new exceptions.rst documents the five-class hierarchy under RedisVLError, clarifies that redis-py errors are wrapped as RedisSearchError with chaining on __cause__, and includes a table mapping each exception to typical entry points (load(), query(), create(), etc.). It also provides four try/except patterns (schema validation on load, query validation, version vs Redis failures, and catching RedisVLError) plus autoclass reference blocks matching the style used elsewhere in the API docs.

Reviewed by Cursor Bugbot for commit 891bfe9. Bugbot is set up for automated code reviews on this repo. Configure here.

Add docs/api/exceptions.rst and link it from the API toctree, so the exception
hierarchy is documented in one discoverable place.

The page covers the five classes in redisvl.exceptions, a table mapping each to
the operations that raise it, and try/except examples for the two cases called
out in the issue: schema validation on load and query validation. Each example
is drawn from an actual raise site, so validate_on_load=True is shown for
SchemaValidationError and ef_runtime on a flat vector field for
QueryValidationError.

Also notes that redis-py exceptions are not part of the hierarchy and that
RedisVL chains the original error, since that is the part most likely to trip
someone writing an except clause.

Fixes redis#496

@vishal-bala vishal-bala left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi! Thanks for contributing to RedisVL.

This looks good overall — the page is well-organized, and I verified the exception hierarchy, table, and examples against the source without finding inaccuracies.

One thing needs fixing before merge: .. currentmodule:: sits below the prose, so 12 of the 14 :class: references render as plain text instead of links — including all five names in the "When each error is raised" table. It's a one-line move; details inline.

Worth knowing why your Definition of Done didn't catch this: your build report is accurate (I reproduced 11 warnings, none referencing exceptions.rst), but Sphinx only reports unresolved Python cross-references under -n. So make docs-build passing can't detect this class of problem — sphinx -b html -n docs docs/_build/html surfaces it.

Two smaller notes inline, both optional.

Comment thread docs/api/exceptions.rst Outdated
Exception classes
=================

.. currentmodule:: redisvl.exceptions

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This directive needs to be above the prose that uses it. Sphinx resolves bare :class:`RedisVLError` against the current module, and until this line there isn't one — so it looks for a top-level RedisVLError while autodoc registered redisvl.exceptions.RedisVLError. Result: 12 unresolved refs (lines 6, 25, 39, 43, 47, 51, 55, 70, 98, 122).

Moving it to just under the page title fixes all 12 at once, and matches how schema.rst orders it. The autoclass blocks below still work, since currentmodule applies to the rest of the file.

For comparison once fixed: the ~redisvl.index.SearchIndex ref on line 60 already links correctly, because it's fully qualified.

Comment thread docs/api/exceptions.rst
***********

RedisVL defines its custom exceptions in ``redisvl.exceptions``. Every one of them
inherits from :class:`RedisVLError`, so catching that single base class is enough to

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"enough to handle any error the library raises on its own behalf" holds for the operations you document, but argument validation happens in constructors, outside that net. Concretely, with the parameter this page features: VectorQuery(..., ef_runtime=-1) raises ValueError: ef_runtime must be positive, not a RedisVLError — and it fires before the try block in the line 107 example opens. A sentence noting that constructor/argument validation raises standard ValueError would set expectations well.

Comment thread docs/api/exceptions.rst
├── RedisSearchError
├── SchemaValidationError
├── QueryValidationError
└── RedisModuleVersionError

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor completeness note: redisvl/mcp/errors.py:22 defines RedisVLMCPError(Exception), which isn't a RedisVLError subclass. Since line 5 reads as exhaustive, either scoping that sentence to the core index/query APIs or adding a pointer would keep this accurate as MCP grows.

@vishal-bala vishal-bala added the auto:documentation Changes only affect the documentation label Aug 6, 2026
vishal-bala pushed a commit that referenced this pull request Aug 6, 2026
## What this fixes

`Timestamp.__ne__`'s docstring promises: *"For date objects (without
time), this excludes the entire day."* The implementation called
`self.between(start, end)` — the **same positive range construction as
`__eq__`** — so the filter matched the entire day instead:

```python
Timestamp("created_at") == date(2023, 3, 17)   # @created_at:[1679029200.0 1679115599.999999]
Timestamp("created_at") != date(2023, 3, 17)   # @created_at:[1679011200.0 1679097599.999999]  ← positive range!
```

Silent inverted filtering: `!=` with a date returns exactly the rows the
caller asked to exclude, with no error or warning. The non-date path of
the same method already negates correctly via `FilterOperator.NE`
(`(-@created_at:[v v])`), as do the `NE` forms of `Tag`, `Text`, `Num`,
and `Geo` — the date branch was the one exception.

There was a second, smaller asymmetry hiding in the same branch:
`__eq__` computes its day bounds with `.astimezone(timezone.utc)` while
`__ne__` used bare `datetime.combine`, so the two methods disagreed
about where the day starts on machines whose local timezone isn't UTC.

## The fix

Compute the day bounds exactly as `__eq__` does (including the
`astimezone` conversion), convert with `_convert_to_timestamp` exactly
as `between()` does, and emit through the existing
`OPERATOR_MAP[FilterOperator.NE]` template. The invariant this
establishes: **`str(ts != d)` is byte-for-byte `f"(-{ts == d})"`** on
any machine, for both `date` objects and date-only ISO strings
(`"2023-03-17"`), which take the same branch. The datetime/unix path is
untouched.

## Tests

Added `test_timestamp_not_equal_date`, which asserts the negated-range
form, the exact-negation-of-`__eq__` invariant, and the ISO-string path.
The existing suite covered `!=` only with a `datetime` (the non-date
path), which is why this never failed. The new test fails on current
`main` and passes with the fix.

`tests/unit/test_filter.py`: 63/63 pass. Full `tests/unit`: 1168 passed,
1 skipped — with 6 errors identical on unmodified `main` in my
environment (Docker-dependent CLI fixtures; no daemon locally). `black`,
`isort`, and `mypy` clean.

## Notes

- Bug-fix, so this should carry the **`auto:patch`** label per
CONTRIBUTING — I can't set labels as an outside contributor.
- Found by reading `filter.py` while working on #654 (exception docs,
still open) — happy to rebase either PR if they land in either order;
they don't overlap.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes query semantics for a previously broken filter path; callers
relying on the old (incorrect) behavior would see different results, but
the fix aligns with documented behavior and other filter types.
> 
> **Overview**
> Fixes **`Timestamp != date`** (and date-only ISO strings) so they
**exclude** the full day instead of matching it. The date branch no
longer calls `between()`; it builds the same UTC day bounds as **`==`**,
converts them with `_convert_to_timestamp`, and emits a negated Redis
range via **`FilterOperator.NE`**—so `str(ts != d)` matches `(-{ts ==
d})`.
> 
> Adds **`test_timestamp_not_equal_date`** to lock in the negated query
shape, the `==` / `!=` pairing, and the ISO-string path.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
1157c08. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Move currentmodule above the prose so the bare :class: references resolve
against redisvl.exceptions; all 12 previously-plain names now link, verified
with sphinx -n. Note that constructor and argument validation raises standard
Python exceptions before any try block opens, and scope the base-class claim
to the core index and query APIs, pointing at the separate RedisVLMCPError.
@TheSaiEaranti

Copy link
Copy Markdown
Contributor Author

Thanks for the thorough review, all three points are in the latest commit.

The currentmodule directive now sits right under the page title, and I removed the duplicate above the autoclass section since it applies to the rest of the file. Verified with the sphinx -n build you suggested: zero unresolved reference warnings for exceptions.rst now. Good to know that make docs-build can't catch this class of problem, I'll use -n for docs changes going forward.

Both optional notes are in too. The intro now scopes the base-class claim to the core index and query APIs with a pointer to redisvl.mcp.errors.RedisVLMCPError, and the note block mentions that constructor validation raises standard ValueError before any try block opens, using your ef_runtime example.

@vishal-bala
vishal-bala merged commit 43fd033 into redis:main Aug 7, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

auto:documentation Changes only affect the documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Document RedisVL exception hierarchy and error handling patterns

2 participants