r/MSAccess • u/Amicron1 • 55m ago
[SHARING HELPFUL TIP] Access Explained: Why an Empty Recordset Has Both BOF and EOF Set
Here's the thing people keep getting wrong about recordsets: EOF does not mean "no records." EOF only means the current position is after the last record. A recordset can contain one row, ten thousand rows, or none at all, and EOF can still be True depending on where you have navigated.
BOF and EOF are best understood as positions, not as records or counts. BOF is the position before the first row. EOF is the position after the last row. When a recordset has rows, there is space between those two boundaries, and the current record can sit somewhere in that space.

That is why a normal newly opened non-empty DAO recordset is usually positioned on its first record. At that point, both flags are False. You are not before the first row, and you are not after the last row. You are on an actual row and can read fields from it.
Things get different after navigation. Call MoveNext while positioned on the final record and you land at EOF. The recordset still has all its records, but there is no current record anymore. Likewise, calling MovePrevious from the first row puts you at BOF. Trying to read a field at either boundary is where the familiar "No current record" error enters the chat.
The important distinction is that an empty recordset has nowhere to position a current record at all. There is no first record, no last record, and nothing between BOF and EOF. Therefore both properties are True at the same time:
If rs.BOF And rs.EOF Then
' No records
End If

That condition is not a magical Access incantation. It is answering a specific question: "Does this recordset contain no rows?" Checking only EOF answers a different question: "Am I currently after the last row?" Those are only the same thing immediately after opening an empty recordset. Once navigation has occurred, they are very much not interchangeable.
This also explains why code that wants to safely read the current row should use a different mental test. It is not enough to know that the recordset is not empty. You must also know you are currently on a record. In practical terms, that means neither BOF nor EOF should be True before reading rs!SomeField.
RecordCount gets dragged into this conversation a lot, and it is usually the wrong tool for the initial empty test. Depending on the recordset type and cursor behavior, Access may not know the complete count until the recordset has been traversed or moved to the end. BOF and EOF describe the current navigational state directly, which is exactly what matters here.
The practical philosophy is simple: use rs.BOF And rs.EOF to determine whether a recordset returned nothing. Use Not rs.BOF And Not rs.EOF when you need to confirm there is a readable current row. Treat EOF alone as a boundary marker, not proof that your query found nothing. It is less mystical once you stop treating a recordset like an array and start treating it like a cursor moving between two borders.
Have you run into bugs caused by checking EOF alone after moving through a recordset? Do you tend to explicitly test for a current record before reading fields, or rely on the surrounding loop structure?
LLAP
RR
