Skip to content

XX000: ResourceOwnerForget called for buffer pin after release started during vchordrq scan on halfvec #463

Description

@r-Larch

Extension versions:

  • vchord 1.1.1 (latest)
  • vector (pgvector) 0.8.1
  • PostgreSQL PostgreSQL 18.2 (Debian 18.2-1.pgdg12+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 12.2.0-14+deb12u1) 12.2.0, 64-bit

Error:
Npgsql.PostgresException: XX000: ResourceOwnerForget called for buffer pin after release started
at resowner.c:572, Routine: ResourceOwnerForget

Setup:

  • Column type: halfvec(1024)
  • Index: vchordrq (MaxInnerProduct / <#> operator)
  • rerank_in_table = true
CREATE INDEX ix_massima_indexes_embedding
    ON massima_indexes
    USING vchordrq (embedding halfvec_ip_ops)
    WITH (
        epsilon = 0.8,
        probes = '480,960',
        options = $$
residual_quantization = false
rerank_in_table = true
build.pin = true

[build.internal]
lists = [8000, 16000]
spherical_centroids = true
build_threads = 8
$$
    );

Trigger: Occurs transiently under concurrent query load.

Workaround: Retrying the query on a fresh connection succeeds 100% of the time.

Stack trace:

An exception occurred while iterating over the results of a query for context type '"Nomos.Judgments.Data.JudgmentDbContext"'."
""Npgsql.PostgresException (0x80004005): XX000: ResourceOwnerForget called for buffer pin after release started
   at Npgsql.Internal.NpgsqlConnector.ReadMessageLong(Boolean async, DataRowLoadingMode dataRowLoadingMode, Boolean readingNotifications, Boolean isReadingPrependedMessage)
   at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token)
   at Npgsql.NpgsqlDataReader.Read(Boolean async, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.BufferedDataReader.BufferedDataRecord.InitializeAsync(DbDataReader reader, IReadOnlyList`1 columns, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.BufferedDataReader.InitializeAsync(IReadOnlyList`1 columns, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.BufferedDataReader.InitializeAsync(IReadOnlyList`1 columns, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.InitializeReaderAsync(AsyncEnumerator enumerator, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.<>c__DisplayClass30_0`2.b__0>d.MoveNext()
--- End of stack trace from previous location ---
   at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.ExecuteImplementationAsync[TState,TResult](Func`4 operation, Func`4 verifySucceeded, TState state, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.ExecuteImplementationAsync[TState,TResult](Func`4 operation, Func`4 verifySucceeded, TState state, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
  Exception data:
    Severity: ERROR
    SqlState: XX000
    MessageText: ResourceOwnerForget called for buffer pin after release started
    File: resowner.c
    Line: 572
    Routine: ResourceOwnerForget"

Activity

  1. VoVAllen commented on Jun 17, 2026

    @VoVAllen
    Contributor

    We’re still not sure about the exact cause, but we suspect it is related to abnormal termination. In other words, your query may have been terminated unexpectedly in some cases, which could have caused some resources to not be released properly. That is what the current error message indicates.

  2. ameyypawar commented on Jul 9, 2026

    @ameyypawar

    I think I found a plausible mechanism — posting in case it helps (couldn't reproduce it, so this is a hypothesis).

    It looks PG18-specific: PG18's rewritten ResourceOwner is what raises ResourceOwnerForget ... after release started, when a buffer pin is released after its owner began ResourceOwnerRelease — which fits the "abnormal termination" hunch (a scan torn down by an abort rather than by amendscan).

    The asymmetry I noticed: PostgresReadStream::drop already guards read_stream_end with !std::thread::panicking() && IsTransactionState() — you already avoid touching buffers when torn down outside a live transaction — but PostgresBufferReadGuard::drop calls UnlockReleaseBuffer unconditionally.

    With the lifetime: ambeginscan stashes the Scanner via CurrentMemoryContext.leak_and_drop_on_delete(...). On a clean scan, amendscan resets scanning (releasing pins while the owner is live); on an aborted scan, amendscan is skipped and the Scanner drops later at memory-context deletion — which in PG's abort sequence is after ResourceOwnerRelease already freed the scan's pins. Any PostgresBufferReadGuard still alive then would UnlockReleaseBuffer an already-released pin → this error.

    I couldn't confirm which pin outlives to that point (depends on the rerank/prefetch buffering I didn't fully trace), so treat the specific pin as a guess — but the unguarded UnlockReleaseBuffer vs. the guarded read_stream_end seemed worth flagging. Fits the symptoms: PG18-only, transient under concurrency, fresh connection always succeeds.

    Fix direction, if so: release the scan's pins at ResourceOwnerRelease (a resource-release/xact-abort callback) instead of via the memory-context callback, or guard the per-guard release like read_stream_end. Happy to test against a repro if you have one.

  3. VoVAllen commented on Sep 2, 2026

    @VoVAllen
    Contributor

    We found the problem might be related to corrupted data in the table. @ytkaradut Can you try REINEX or REINDEX concurrently for the index?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions