Contents
This page in other versions: Development

Warning: this documentation is for a pre-release version of pgAdmin 4.

Version 9.18

Release date: 2026-09-17

This release contains a number of bug fixes and new features since the release of pgAdmin 4 v9.17.

Supported Database Servers

PostgreSQL: 14, 15, 16, 17 and 18

EDB Advanced Server: 14, 15, 16, 17 and 18

Bundled PostgreSQL Utilities

psql, pg_dump, pg_dumpall, pg_restore: 18.4

New features

Issue #9631 - Collapse and restore the Object Explorer by re-clicking the current workspace icon, in the manner of the VS Code side bar, remembering the choice across refreshes. A keyboard shortcut, Ctrl+Alt+B by default, does the same thing and can be changed through the new toggle_object_explorer preference.

Housekeeping

Issue #10221 - Skip importing and initialising the kerberos, ldap, mfa, oauth2 and webserver authentication providers unless SERVER_MODE is set, leaving desktop mode with internal authentication alone.
Issue #10247 - Relax the azure-mgmt-resource pin to allow 24.0.0, and aggregate the third-party JavaScript and Python dependency bumps for this release.
Issue #10293 - Make the Schema Diff regression test assert its own generated script, which it previously swallowed, so invalid generated SQL can no longer pass silently.

Bug fixes

Issue #9226 - Accept SharedUsername when importing a shared server from a servers.json definition, instead of insisting on Username for every server.
Issue #10155 - Share concurrent identical GET requests behind getNodeAjaxOptions() so a wide table’s Columns tab no longer fires one duplicate get_types request per column row.
Issue #10179 - Fix inherited columns in the Table dialog being editable and deletable: a column already inherited from a parent carries inheritedfromtable while one fetched interactively through ‘Inherited from table(s)’ carries inheritedfrom and has no attnum yet, and only the latter was checked, after an isNew() short-circuit that treated the attnum-less rows as new.
Issue #10180 - Fix the Data type dropdown on the expanded Definition tab of the Table dialog offering every type, ignoring the allowed-type restriction already applied to the inline editor.
Issue #10214 - Fix the existingSecret path in the Helm deployment template.
Issue #10235 - Remove a trailing quote from the Windows installer’s ProductVersion, which was stamped as e.g. 9.17".
Issue #10236 - Fix Schema Diff reporting false differences for SERIAL/BIGSERIAL columns by ignoring the owned sequence’s oid, and fix the invalid ALTER COLUMN ... TYPE bigserial SQL generated when such a column genuinely differs.
Issue #10237 - Fix a replace() argument 2 must be str, not None crash when a per-server Password Exec Command is used with a service-only (pg_service.conf) connection, which leaves host, port and username unset.
Issue #10239 - Remove a redundant passlib pin that conflicts with the indirectly required libpass.
Issue #10252 - Fix the argument grid on the Definition tab of a user-defined function or procedure refusing to delete, or add, a row in edit mode.
Issue #10295 - Reinstate dependency ordering of the script Schema Diff generates, which had been lost since the React port left dependLevel unset.
Issue #10297 - Fix Schema Diff duplicating any column that also differs when it recreates a foreign table.
Issue #10298 - Fix Schema Diff generating SQL that PostgreSQL rejects when a sequence’s MINVALUE is raised above its current value.
Issue #10300 - Fix a foreign table column added by Schema Diff losing its collation.
Issue #10302 - Fix Schema Diff injecting whitespace into an applied function or procedure body, which left a whitespace-only difference behind.
Issue #10303 - Report a Schema Diff comparison that fails part way through as a failure, rather than as a success.
Issue #10309 - Reject an empty or null Username when importing a non-shared server, which previously imported cleanly and left a server libpq would silently authenticate as the OS account running pgAdmin.
Issue #10311 - Fix login being impossible against Flask-Security-Too 5.8.2, which corrected a long-standing inversion in UserMixin.is_locked() that User.is_locked() had been written against.
Issue #10341 - Omit the redundant TABLESPACE pg_default clause from generated index SQL, which was invalid on a partitioned table.
Issue #10383 - Fix an authentication bypass in Webserver authentication mode, where get_user() fell back to reading the configured WEBSERVER_REMOTE_USER name from the request headers when it was absent from the WSGI environment, letting any client that could reach pgAdmin assert any identity, including an administrator’s (CVE-2026-86863). Header-asserted identity is now opt-in, restricted to a configured list of trusted proxies with an optional shared secret, and refused for accounts whose authentication source is not webserver. Reported by Sanghyeon Lee (@h9e0n).
Issue #10384 - Fix argument and connection-string injection in the Backup tool, where the client-supplied database name was appended to the pg_dump argument vector as a bare positional argument: because getopt_long permutes arguments, a value beginning with a dash supplied further options such as --file, overriding the storage-confined output path, and because libpq expands a database name containing an equals sign into a full connection string, one could also redirect the connection to an attacker-controlled host and exfiltrate the stored password (CVE-2026-86864). The database name is now passed through the PGDATABASE environment variable instead. Reported by Sanghyeon Lee (@h9e0n) and Hitesh Jambhale.
Issue #10385 - Fix a time-of-check to time-of-use flaw in the File Manager’s save_file endpoint, which backs saving from the Query Tool and ERD: the requested path was validated with check_access_permission() and then opened with a plain open(), so a symbolic link planted in between was followed, writing outside the storage directory. This is the sink CVE-2026-7819’s hardening of the separate upload path did not cover (CVE-2026-86861). Reported by sec-rex.
Issue #10388 - Fix connection-string injection in the Restore and Maintenance tools, where the client-supplied database name was passed straight to --dbname: libpq expands a database name containing an equals sign into a full connection string, whose embedded keywords override the --host and --port pgAdmin supplies, so the connection - and the stored password exported in PGPASSWORD - could be redirected to a server of the caller’s choosing (CVE-2026-86862). The database name is now passed through the PGDATABASE environment variable, which libpq never expands. Found by Hitesh Jambhale.
Issue #10393 - Render the Validate binary path dialog as HTML, instead of showing the raw markup.
Issue #10420 - Refuse HTTP redirects on LLM API requests, rather than following a Location header on to a destination ALLOWED_LLM_API_URLS was never applied to. This is hardening rather than a fix for an exploitable flaw, since returning the redirect at all requires control of a host already on the allowlist. Reported by Ziya Abdullayev.

Dependencies

Non-breaking dependabot and audit-driven bumps aggregated for v9.18.

Python:

Authlib 1.7.* -> 1.8.*
azure-mgmt-resource ==25.0.0 -> >=24.0.0,<26.0.0
certifi 2026.6.17 -> 2026.7.22
cryptography 49.0.* -> 50.0.*
Flask-Security-Too 5.8.* -> >=5.8.2,<5.9
google-auth-oauthlib 1.4.0 -> 1.4.1
gssapi 1.11.* -> 1.12.*
passlib 1.* pin removed (redundant, and conflicting with libpass)
psycopg[c] 3.3.4 -> 3.3.5
selenium (web/regression) 4.45.0 -> 4.48.0
setuptools 83.* -> 84.*
testscenarios (web/regression) 0.6.2 -> 0.7.0
typer 0.26.* -> 0.27.*
Skipped: paramiko 3 -> 5 (blocked on sshtunnel’s paramiko.DSSKey usage) and azure-mgmt-resource 26.0.0 (drops Python 3.9 support)

JavaScript (web/):

@babel/* toolchain (core, eslint-parser, eslint-plugin, plugin-syntax-jsx, plugin-transform-class-properties, plugin-transform-object-rest-spread, plugin-transform-runtime, preset-env, preset-react, preset-typescript) -> 7.29.7
@mui/icons-material / @mui/material 7.3.10 -> 7.3.11
@mui/x-date-pickers 8.28.3 -> 8.29.3
@tanstack/react-query 5.101.4 -> 5.102.8
@tanstack/react-virtual 3.13.24 -> 3.14.10
@testing-library/jest-dom 6.9.1 -> 6.10.0
@testing-library/react 16.3.2 -> 16.3.3
@testing-library/user-event 14.6.1 -> 14.6.3
@types/react 19.2.17 -> 19.2.18
@types/react-dom 19.2.3 -> 19.2.5
anti-trojan-source 1.12.0 -> 1.12.2
axios 1.18.1 -> 1.20.0
codemirror 6.0.2 -> 6.65.7
core-js 3.49.0 -> 3.50.0
css-loader 7.1.4 -> 7.1.5
diff-arrays-of-objects 1.1.10 -> 1.1.11
dompurify 3.4.12 -> 3.4.14
eslint 9.39.4 -> 9.39.5
eslint-plugin-jest 29.15.5 -> 29.16.6
globals 17.7.0 -> 17.12.0
hotkeys-js 4.0.4 -> 4.0.7
image-minimizer-webpack-plugin 4.1.4 -> 5.0.0
ip-address 10.2.0 -> 10.7.0
jest / jest-environment-jsdom 30.4.2 / 30.4.1 -> 30.5.1
lossless-json 4.3.0 -> 4.3.1
marked 18.0.7 -> 18.0.11
papaparse 5.5.4 -> 5.7.0
postcss 8.5.20 -> 8.5.26
rc-dock 4.0.0-alpha.2 -> 4.0.0-alpha.3
react / react-dom 19.2.7 -> 19.2.8
react-arborist 3.5.0 -> 3.16.0
react-draggable 4.7.0 -> 4.7.1
sharp 0.35.3 -> 0.35.4
svgo 4.0.2 -> 4.1.0
svgo-loader 4.0.0 -> 5.0.0
typescript-eslint 8.65.0 -> 8.69.0
use-resize-observer 9.1.0 -> 10.0.0
vanilla-jsoneditor 3.12.0 -> 3.13.0
webpack-bundle-analyzer 5.3.1 -> 5.3.2
zustand 5.0.14 -> 5.0.15
Transitive advisory bumps: brace-expansion, fast-uri, js-yaml, shell-quote, socket.io-parser, tar
Held back: @simonwep/pickr 1.10.x (webpack production-bundle interop break), Babel 8, and webpack 5.110.3 (npm-quarantined at bump time)

JavaScript (runtime/):

axios 1.18.1 -> 1.19.0
electron 43.1.1 -> 43.4.0
eslint 10.7.0 -> 10.8.1
globals 17.7.0 -> 17.11.0
ip-address 10.2.0 -> 10.3.1
postcss 8.5.20 -> 8.5.23
Transitive advisory bumps: brace-expansion, fast-uri, undici