{"id":"3e7ab340ecea96ad","repo":"jackc/pgx","slug":"no-reference-to-batch","errorCode":null,"errorMessage":"no reference to batch","messagePattern":"no reference to batch","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"batch.go","lineNumber":443,"sourceCode":"\t}\n\n\tbr.closed = true\n\n\terr := br.pipeline.Close()\n\tif br.err == nil {\n\t\tbr.err = err\n\t}\n\n\treturn br.err\n}\n\nfunc (br *pipelineBatchResults) earlyError() error {\n\treturn br.err\n}\n\nfunc (br *pipelineBatchResults) nextQueryAndArgs() (query string, args []any, err error) {\n\tif br.b == nil {\n\t\treturn \"\", nil, errors.New(\"no reference to batch\")\n\t}\n\n\tif br.qqIdx >= len(br.b.QueuedQueries) {\n\t\treturn \"\", nil, errors.New(\"no more results in batch\")\n\t}\n\n\tbi := br.b.QueuedQueries[br.qqIdx]\n\tbr.qqIdx++\n\treturn bi.SQL, bi.Arguments, nil\n}\n\ntype emptyBatchResults struct {\n\tconn   *Conn\n\tclosed bool\n}\n\n// Exec reads the results from the next query in the batch as if the query has been sent with Exec.\nfunc (br *emptyBatchResults) Exec() (pgconn.CommandTag, error) {","sourceCodeStart":425,"sourceCodeEnd":461,"githubUrl":"https://github.com/jackc/pgx/blob/ec1a0befd22592cffffdeeb0a50311b506372f4c/batch.go#L425-L461","documentation":"Returned by (*pipelineBatchResults).nextQueryAndArgs when br.b is nil — the pipeline BatchResults object has no pointer back to the originating Batch. pgx constructs pipelineBatchResults with b set inside SendBatch, so a nil batch indicates an internal invariant violation or a result object that was not produced through the normal SendBatch path. The reader cannot correlate the next server result with any queued query.","triggerScenarios":"Calling Exec()/Query()/QueryRow() on a *pipelineBatchResults whose Batch pointer is nil. In normal API use SendBatch always sets it, so this surfaces from internal construction bugs, reflection/test doubles that build pipelineBatchResults without b, or a future code path that sends a pipeline query without a Batch.","commonSituations":"Test scaffolding or wrappers that synthesize BatchResults manually; an internal pgx bug after a refactor; calling methods on a pipelineBatchResults struct created directly rather than via SendBatch.","solutions":["Only obtain BatchResults from Conn.SendBatch (or the pool/tx equivalents); never construct pipelineBatchResults yourself.","Report this as a pgx bug if reached through the public API — the batch pointer should always be populated.","If writing a custom BatchResults implementation for testing, return emptyBatchResults or your own type rather than reusing pipelineBatchResults.","Update to the latest pgx patch release in case the invariant was restored."],"exampleFix":"// before (test double)\nbr := &pipelineBatchResults{ctx: ctx, conn: c, pipeline: p} // missing b\n_, err := br.Exec()\n\n// after\nbr := &pipelineBatchResults{ctx: ctx, conn: c, pipeline: p, b: batch}","handlingStrategy":"validation","validationCode":"// Only ever obtain BatchResults from SendBatch:\nif batch == nil || len(batch.QueuedQueries) == 0 {\n    // send nothing / use emptyBatchResults semantics\n    return nil\n}\nbr := conn.SendBatch(ctx, batch)","typeGuard":null,"tryCatchPattern":"if _, err := br.Exec(); err != nil {\n    if strings.Contains(err.Error(), \"no reference to batch\") {\n        return fmt.Errorf(\"internal: BatchResults has no batch; report upstream\")\n    }\n    return err\n}","preventionTips":["Never construct pipelineBatchResults by hand.","Obtain BatchResults only through Conn/Pool/Tx SendBatch.","Treat 'no reference to batch' as an internal-invariant bug and report it."],"tags":["batch","pipeline","internal-invariant"],"analyzedSha":"ec1a0befd22592cffffdeeb0a50311b506372f4c","analyzedAt":"2026-08-04T22:52:11.263Z","schemaVersion":2}