bytes.rindex()

rfind with the raising failure mode. Use it when the separator is required and its absence means the input is malformed.

Bytes methodPython 3.0+Live demo
Common call
i = path.rindex(b'/')
Returns
int — byte offset from the left; absence raises
Replaces
rfind plus a manual -1 check
Watch out
the message says "subsection", not "substring"
bytes.rindex(sub[, start[, end]])
→ int

Demo

Live evaluation
Try:
Inputs
sstrdata (encoded as utf-8)
substrsequence to locate
Output
bytes('abcabc', 'utf-8').rindex(bytes('b', 'utf-8'))
4

Same reverse scan as rfind, same left-counted offset — abcabc gives 4 for b, the last occurrence. The difference is the failure case: a missing sequence raises ValueError rather than returning -1. Choose rindex when the thing you are looking for must be there, so that its absence stops the program instead of producing a plausible -1.

Parameters

NameTypeRequiredDescription
subbytes | intyesByte sequence to locate. An int from 0 to 255 searches for that single byte.
startintno (0)Left boundary of the searched region.
endintno (len)Right boundary, exclusive.

Return value

int — Byte offset of the last occurrence. Raises ValueError with the message "subsection not found" when absent.

Common patterns

Required last separator
A path that must contain a slash; absence is an error.
i = path.rindex(b'/')
directory, leaf = path[:i], path[i + 1:]
Convert absence into a domain error
Catch and re-raise with a message the caller can act on.
try:
    i = ref.rindex(b':')
except ValueError:
    raise ValueError('expected host:port')
Prefer rpartition for splitting
No offset to manage, and no exception when the separator is optional.
head, sep, tail = ref.rpartition(b':')

Examples

1. Last of two
b'abcabc'.rindex(b'b')
Returns
4
2. Single
b'abc'.rindex(b'b')
Returns
1
3. Absent raises
b'abc'.rindex(b'z')
Returns
ValueError: subsection not found
4. Empty sub
b'abc'.rindex(b'')
Returns
3
5. Bounded
b'abcabc'.rindex(b'b', 0, 4)
Returns
1
6. rfind returns -1
b'abc'.rfind(b'z')
Returns
-1 # the alternative

Pitfalls

1. It raises where rfind returns -1
Identical signature, different failure. Swapping one for the other without adjusting the error handling turns a quiet -1 into an uncaught exception.
Uncaught
b'abc'.rindex(b'z')
ValueError: subsection not found
rfind for optional
i = b'abc'.rfind(b'z')
if i == -1:
    ...
-1, handled
2. The offset is from the left
Only the scan is reversed. The number is an ordinary index, not a distance from the end.
Not from the end
b'abcabc'.rindex(b'b')
4
Convert if needed
len(d) - d.rindex(b'b') - 1
1
3. A str argument is a TypeError, not a ValueError
Two different failures. A handler written for the not-found case does not catch the type error from passing a str.
Wrong handler
try:
    b'abc'.rindex('z')
except ValueError:
    ...
TypeError escapes
Encode the needle
b'abc'.rindex('z'.encode())
ValueError, as expected

When to use

Use it
  • A required trailing separator whose absence is an error
  • Parsing where a missing marker means malformed input
Reach for something else
  • Absence is normal → rfind
  • Splitting on the separator → rpartition
  • The FIRST occurrence → index

Notes

Complexity
O(n * m) worst case; the same reverse search as rfind
Return
A non-negative byte offset; absence raises
CPython impl
Objects/bytesobject.c :: bytes_rindex
Memory
No allocation — scans in place
Thread-safe
Yes — bytes are immutable

FAQ

rindex when a missing value is a bug — you get a ValueError at once. rfind when absence is ordinary — you get -1 and decide what to do. If you would write "if i == -1: raise" anyway, rindex already does it.

History

3.0
bytes.rindex arrived with the bytes type in the text/binary split.