jackc/pgx · error

short write to large object

Error message

short write to large object

What it means

Returned by LargeObject.Write when the server's lowrite reported a non-negative count strictly less than the bytes requested (large_objects.go:99-100). The server accepted fewer bytes than offered, which pgx treats as an error because the writer contract for io.Writer plus the chunking loop expects the full slice to be consumed. It indicates the large object is at capacity/exceeded its storage or the server truncated the write.

Source

Thrown at large_objects.go:100

			break
		} else if expected > maxLargeObjectMessageLength {
			expected = maxLargeObjectMessageLength
		}

		var n int
		err := o.tx.QueryRow(o.ctx, "select lowrite($1, $2)", o.fd, p[nTotal:nTotal+expected]).Scan(&n)
		if err != nil {
			return nTotal, err
		}

		if n < 0 {
			return nTotal, errors.New("failed to write to large object")
		}

		nTotal += n

		if n < expected {
			return nTotal, errors.New("short write to large object")
		} else if n > expected {
			return nTotal, errors.New("invalid write to large object")
		}
	}

	return nTotal, nil
}

// Read reads up to len(p) bytes into p returning the number of bytes read.
func (o *LargeObject) Read(p []byte) (int, error) {
	nTotal := 0
	for {
		expected := len(p) - nTotal
		if expected == 0 {
			break
		} else if expected > maxLargeObjectMessageLength {
			expected = maxLargeObjectMessageLength
		}

View on GitHub (pinned to ec1a0befd2)

Solutions

  1. Retry the unwritten tail (the chunking loop already caps each lowrite at maxLargeObjectMessageLength; if you bypassed it, re-enable chunking).
  2. Confirm the large object storage is healthy and the descriptor is still valid (re-open if the transaction ended).
  3. If reproducible, capture the exact n vs. expected to file a pgx/PostgreSQL investigation — partial writes from lowrite are unusual.
  4. Avoid writing more than maxLargeObjectMessageLength per call so the loop's chunking handles it.

Example fix

// before — bypassing the chunk loop with one huge Write
_, err := lo.Write(hugeSlice) // may surface 'short write'

// after — let pgx chunk, or split yourself under maxLargeObjectMessageLength
chunk := maxLargeObjectMessageLength
for off := 0; off < len(data); off += chunk {
    end := off + chunk; if end > len(data) { end = len(data) }
    if _, err := lo.Write(data[off:end]); err != nil { return err }
}
Defensive patterns

Strategy: retry

Validate before calling

// keep each Write under the protocol chunk cap so the loop can chunk cleanly
const chunk = 1024*1024*1024 - 1024 // maxLargeObjectMessageLength
if len(p) > chunk {
    // split before writing
}

Try / catch

// io.Writer contract; retry the tail once, then surface
n, err := lo.Write(p[written:])
if err != nil && err.Error() == "short write to large object" {
    // unusual; investigate server state
}
written += n

Prevention

When it happens

Trigger: Writing a chunk where lowrite returns n with 0 <= n < expected — the server wrote fewer bytes than the payload supplied in that lowrite call.

Common situations: Exceeding the large object size the server can hold in one operation; a server/extension quirk returning a partial count; running near the per-message or per-large-object limits.

Related errors


AI-assisted analysis of jackc/pgx@ec1a0befd2 (2026-08-04). Data as JSON: /data/errors/a9d44cf9ae0c3057.json. Report an issue: GitHub.