# 58030 — io_error

> Source-backed full reference for PostgreSQL SQLSTATE 58030.
---

# 58030

## At a glance {#at-a-glance}

`58030` is emitted by a specific shared-buffer cleanup path after a write failure has already been recorded. In PostgreSQL 18.6 that path reports `WARNING`, not a generic client `ERROR`: it names the block and relation and says that repeated failure may be permanent.

<!-- BEGIN SQLSTATE FACTS: generated by scripts/generate.py; do not edit -->

| Field | Value |
| --- | --- |
| SQLSTATE | `58030` |
| Condition | `io_error` |
| Status | `active` |
| Known present by | `7.4` |
| Locked snapshots | `9.0.23, 9.1.24, 9.2.24, 9.3.25, 9.4.26, 9.5.25, 9.6.24, 10.23, 11.22, 12.22, 13.23, 14.24, 15.19, 16.15, 17.11, 18.6, 19beta3` |
| Macros | `ERRCODE_IO_ERROR` |
| Aliases | `—` |

<!-- source facts: data/errcodes/58030.json -->
<!-- END SQLSTATE FACTS -->

## Meaning {#meaning}

`FlushBuffer` starts the shared-buffer write, flushes WAL first for permanent relations, and calls `smgrwrite`. If error unwinding reaches the resource-owner buffer-I/O callback, `AbortBufferIO` marks the buffer with `BM_IO_ERROR`. When the buffer is still valid and dirty and this is not the first failure, it emits SQLSTATE `58030` at `WARNING` level, then terminates the buffer I/O with the error flag. The earlier `smgrwrite` failure is the event that may have aborted the client statement; this later warning is a repeat-failure notice and does not by itself establish that the client transaction is in error.

The relation text is generated from the buffer tag (`relpathperm`), while the write callback can add the context `writing block %u of relation "%s"` to the original error. The message therefore identifies where to investigate, but it is not a promise that the block is the only damaged object.

A separate storage-manager path extends a relation segment with `FileWrite`. If that call returns a negative result, `md.c` raises `ERROR` with `could not extend file "%s": %m` and `errcode_for_file_access()`; the saved `EIO` errno maps to `58030`, while `ENOSPC` maps to `53100`. This original `ERROR` can abort a client statement and put an explicit transaction in the failed state. It is a different emission from the later repeated-failure `WARNING` in `AbortBufferIO`.

## Messages {#messages}

- **WARNING**, SQLSTATE `58030`: `could not write block %u of %s`
  - Detail: `Multiple failures --- write error might be permanent.`
- Error context used around shared-buffer writes: `writing block %u of relation "%s"`.
- **ERROR**, SQLSTATE selected by `errcode_for_file_access()`; for saved `EIO`, `58030`: `could not extend file "%s": %m`
  - Hint: `Check free disk space.`

The first write failure can have a different primary message and SQLSTATE, for example one selected by an OS errno helper. Preserve that first record together with this warning. In the relation-extension path, `EIO` deliberately selects this condition, whereas `ENOSPC` selects `53100`; do not collapse those errno branches into a generic disk-full diagnosis.

## Diagnosis {#diagnosis}

Keep the complete error chain, including the first write error, this warning, server-log context, relation fork and block, filesystem or device errno, mount state, and whether WAL or a replica has confirmed the write. Check whether the failing operation was a backend statement, checkpoint, background writer, or another server path. A source-only review cannot identify which storage component failed on a particular deployment.

## Response {#response}

Stop retrying while the storage path is unhealthy. Verify the mount, device, permissions, space and quota, and storage error logs; preserve the affected relation and replica evidence before any repair. The relation-extension `ERROR` can leave an explicit client transaction failed, so issue `ROLLBACK` or `ROLLBACK TO` an already-established savepoint before sending more SQL. The repeated-failure `WARNING` from a background or cleanup path does not by itself require a client rollback, but it still requires storage remediation and a durability check. Retry the whole idempotent unit only after the cause is fixed; if the backend or server stopped, use a new connection and validate commit state before replaying work.

## Versions {#versions}

The locked catalogue records this condition from 7.4; the fixed mechanism and message role are read from PostgreSQL 18.6. No natural I/O failure was induced for this source-only entry.

## Related {#related}

[`53100`](../53100/), [`58P03`](../58p03/), [`XX001`](../xx001/)

## Sources {#sources}

[`src/backend/storage/buffer/bufmgr.c#L4307-L4436`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/storage/buffer/bufmgr.c#L4307)

[`src/backend/storage/buffer/bufmgr.c#L6164-L6225`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/storage/buffer/bufmgr.c#L6164)

[`src/backend/storage/smgr/md.c#L512-L519`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/storage/smgr/md.c#L512)

[`src/backend/utils/error/elog.c#L867-L942`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/error/elog.c#L867)

The structured [evidence record](../data/evidence/58030.json) records the fixed warning, detail, source boundary, and lack of natural runtime evidence.
