What does HRESULT 0x87AF0064 (SQLITE_E_ROW) mean?

 
Previous Next
SQLITE_E_WARNING SQLITE_E_DONE

SQLITE_E_ROW

SQLITE_E_ROW (0x87AF0064) represents sqlite3_step has produced one result row. This HRESULT wraps SQLite result code 100. It is a control-flow result, not an error: column values for the current row are available until the statement is stepped again, reset, or finalized. For SQLITE_E_ROW, apply the native SQLite state-machine rules even though Windows stores the result in an HRESULT facility.

Correct loop contract

  1. Call sqlite3_step on a prepared statement
  2. Read every required column while the result is SQLITE_ROW
  3. Step again until SQLITE_DONE or an actual error is returned

Lifetime rules

ObjectBoundary
Column pointersText and blob pointers are tied to the current statement row.
BindingsBindings normally remain until cleared or rebound, but result data does not.
Reset/finalizeReset permits reuse; finalize releases the prepared statement.

Common bug pattern

Code that treats every nonzero SQLite result as failure will reject valid rows. The opposite bug is to process one row and assume the statement is complete without stepping to SQLITE_DONE.

Proof

A regression query with multiple rows must deliver each row once, end in DONE, and release or reset the statement on every exit path.

Read columns before advancing

Call sqlite3_column_type before conversions when NULL and dynamic typing matter, and copy text or blob data that must outlive the current row. A subsequent step, reset, or finalize can invalidate pointers returned by the column accessors even though the database connection remains open. Also verify that every expected column index is within the prepared statement’s result shape; a changed SELECT list is a contract change, not a ROW-state failure. Include an empty-result query and a row containing SQL NULL in the same regression suite.

Two independent axes for SQLITE_E_ROW

ComparisonWhat it reveals
Same artifact, different implementationWhether the representation is independently rejected
Known-good artifact, same environmentWhether the local stack fails without production-specific input
Original operation, one isolated changeWhether the proposed correction controls the observed result
Deliberate boundary violationWhether validation remains active after the change

Safe logging for SQLITE_E_ROW

Capture SQLITE_E_ROW with the operation, byte count, parser or statement position, and contract version, replacing sensitive bodies with hashes and bounded excerpts without losing the state needed to reproduce the event.

SQLite object lifetimes for SQLITE_E_ROW

ObjectRecord
Database connectionOpen flags, threading mode, transaction state, and latest connection error
Prepared statementSQL identity, bindings, prepare API, and step call
Current rowColumn values governed by the documented SQLITE_ROW lifetime
Log callbackExtended code and message retained even when the active API succeeds

Native SQLite identity behind SQLITE_E_ROW

Keep the primary and extended SQLite result before mapping it to SQLITE_E_ROW, because masking the low byte or treating every HRESULT alike can erase the distinction among ROW, DONE, NOTICE, WARNING, and an extended warning.

Cleanup paths for SQLITE_E_ROW

  • Finalize every statement that will not be reused
  • Reset reusable statements only at a documented terminal state
  • Avoid recursive SQLite use from the configured log callback
  • Verify rollback, commit, and close independently from this result

Decision boundary for SQLITE_E_ROW

EvidenceUse
Direct API returnDetermines control flow for prepare, step, reset, finalize, or transaction work
Extended result codePreserves the specific SQLite condition before HRESULT translation
Log callback eventAdds diagnostics but does not automatically replace the direct API result
Transaction outcomeSeparately determines whether completed statement work becomes durable

Technical references for SQLITE_E_ROW


Looking for a different code? Search another status or error code.