PostgreSQL · 错误码速查
PostgreSQL could not access file "$libdir/…":服务端在自己的库目录里找不到这个 .so,先查是哪套安装、哪个库在引用
ERROR: could not access file "$libdir/pg_trgm": No such file or directory(SQLSTATE 58P01 undefined_file)的意思是:正在运行的这套 PostgreSQL 去它自己的包库目录($libdir,以服务端 pg_config 视图里的 PKGLIBDIR 为准)找这个共享库,没找到。常见于大版本升级后新版本没装对应扩展包、换了安装路径,或 shared_preload_libraries 写错(这时服务直接起不来)。修法只有两条:给当前服务端版本装上对应构建的库,或删掉引用它的扩展和函数。我的判断:升级前在每个库按 pg_proc.probin 列一张库文件清单,比升级后看报错救火省事;把旧版本的 .so 拷过去不是解法,测试库里换来的是 version mismatch 或 undefined symbol。
官方原文
could not access file "%s": %m出自 PostgreSQL 源码 src/backend/utils/fmgr/dfmgr.c 的 internal_load_library()(REL_17_STABLE):stat() 找不到文件时报出,errcode_for_file_access() 把 ENOENT 映射为 58P01 undefined_file。官方文档 C-Language Functions 36.10.1 对 $libdir 的说明原文:If the name starts with the string $libdir, that part is replaced by the PostgreSQL package library directory name, which is determined at build time.
出处:PostgreSQL 源码 REL_17_STABLE:src/backend/utils/fmgr/dfmgr.c(internal_load_library) 最后核对:
| SQLSTATE | 58P01(undefined_file,Class 58 System Error) | 出处 |
|---|---|---|
| $libdir 指什么 | 编译时确定的包库目录;文档说可用 pg_config --pkglibdir 查,多套安装时以服务端 pg_config 视图为准 | 出处 |
| 同一函数的其他报错 | 权限不足 42501、路径是目录 42809、.so 版本不对或缺符号 XX000、库在但函数名不在 42883(测试库实测) | 出处 |
| pg_upgrade 要求 | 旧集群用过的扩展,要在新集群装上「与新服务端二进制匹配」的共享库 | 出处 |
| 测试环境 | 测试库、自造数据,PG 16.16/17.12(apt.postgresql.org 的 Debian 包解包到实验目录,lc_messages=C),非客户环境 | |
| 最后核对 | 2026-11-13 |
盈尺实测(测试库,非官方文档):2026-11-13 在 box 本机 PostgreSQL 17.12 与 16.16 测试库(自造表 sku 2 万行、自造库 shop)复现 58P01 及相邻报错、.so 被删后的会话表现、预加载写错、pg_upgrade 16.16→17.12 --check 缺库与修复。 本页第一段:测试库复现输出 →
同一句报错,这几种写法都会触发(PG 17.12 / 16.16 测试库实测)
2026-11-13 我在 box 本机起了 PostgreSQL 17.12 和 16.16 两个测试实例(测试库、自造数据,PG 16.16/17.12(apt.postgresql.org 的 Debian 包解包到实验目录,lc_messages=C),非客户环境),用 \set VERBOSITY verbose 把 SQLSTATE 和源码位置一起打出来。下表每一行都是 psql 原样输出,单条耗时 25–37 ms(含建连接):
| 我执行的语句 | 报错原文 | SQLSTATE |
|---|---|---|
| CREATE FUNCTION … LANGUAGE C AS '$libdir/inch_nolib' | could not access file "$libdir/inch_nolib": No such file or directory | 58P01 |
| 同上,写裸名 'inch_nolib' | could not access file "inch_nolib": No such file or directory | 58P01 |
| 同上,写绝对路径 '/opt/inch/inch_nolib.so' | could not access file "/opt/inch/inch_nolib.so": No such file or directory | 58P01 |
| LOAD '$libdir/inch_nolib' | could not access file "$libdir/inch_nolib": No such file or directory | 58P01 |
| CREATE EXTENSION inch_demo(自造:control 和 SQL 脚本在,.so 不在) | could not access file "$libdir/inch_demo": No such file or directory | 58P01 |
| 已装 pg_trgm 后把 pg_trgm.so 改名,新开会话调用 similarity() | could not access file "$libdir/pg_trgm": No such file or directory | 58P01 |
| PG 16.16 上同样的 CREATE FUNCTION | 措辞相同 | 58P01 |
结论:报错里引号中的字符串就是 pg_proc.probin 里存的原样路径($libdir/ 前缀没展开),所以拿它去全库搜「是谁在引用」最快,见下文「升级前清单」一节。
老连接照常、新连接报错:.so 被删后为什么像「偶发故障」
我先在 17.12 测试库装 pg_trgm 1.5,建表 sku(2 万行自造数据)和 GIN 三元组索引 sku_title_trgm,然后把 pg_trgm.so 改名,模拟「包被卸载 / 新版本没装这个扩展」。结果按连接分成两派:
| 会话与语句 | 结果 |
|---|---|
| 改名前已经调用过 similarity() 的同一会话,再调用一次 | 正常,返回 0.78571427 |
| 新会话 SELECT similarity('iphone case','iphone cases') | 58P01 could not access file "$libdir/pg_trgm" |
| 新会话,关掉顺序扫描后 EXPLAIN / 执行 title LIKE '%case 1999%'(会走 GIN 索引) | 58P01(EXPLAIN 阶段就报) |
| 新会话 INSERT INTO sku VALUES (20001,'new item')(表上有三元组索引) | 58P01 |
| 新会话 SELECT count(*) FROM sku(不碰索引) | 正常,20000 |
这解释了线上常见的错觉:连接池里活得久的连接早就把库载入了内存,照常工作;新建的连接才报错,于是看起来时好时坏。写入也会挂——表上只要有依赖这个扩展的索引,INSERT 就报,而简单的 count(*) 不受影响,容易被误判成「数据没问题、是应用的 bug」。
把文件放回原处后,新会话立即恢复(similarity() 27 ms 返回、索引查询 30 ms 返回 11 行),不需要重启;但老连接里载入的仍是旧文件,换了新版本 .so 时最好让连接池重建连接。
ALTER EXTENSION UPDATE 成功,不能证明库文件已就位
很多升级手册把「跑一遍 ALTER EXTENSION … UPDATE 没报错」当成扩展验收。我在 pg_trgm.so 已被改名的状态下执行 ALTER EXTENSION pg_trgm UPDATE,29 ms 返回 ALTER EXTENSION,版本从 1.5 变成了 1.6——而此时任何新会话一调用它就报 58P01。
原因在升级脚本:17 自带的 pg_trgm--1.5--1.6.sql 只有两条 ALTER OPERATOR FAMILY … ADD OPERATOR,不创建也不调用 C 函数,所以根本不加载 .so。别的扩展的升级脚本未必如此,但结论一样:UPDATE 只说明脚本跑完了。
我用的验收方式:新开一个连接,调用一次该扩展里的 C 函数,再跑一条会走它索引的查询;两条都返回才算库在。版本差用只读 SQL 查(测试库在 1.5 时输出 pg_trgm installed 1.5 / available 1.6):
SELECT e.extname, e.extversion AS installed, a.default_version AS available
FROM pg_extension e
LEFT JOIN pg_available_extensions a ON a.name = e.extname
ORDER BY 1;$libdir 到底是哪个目录:别信你 shell 里的 pg_config
官方文档说 $libdir 实际指向的目录可以用命令 pg_config --pkglibdir 查。但机器上装了不止一套 PostgreSQL 时(发行版包 + 官方源包、多个大版本、容器外再装一套客户端),命令行的 pg_config 不一定属于正在运行的服务端。测试机上正好是这种情况:
| 查法 | 测试机输出 |
|---|---|
| /usr/lib/postgresql/17/bin/pg_config --version --pkglibdir(系统里的客户端包) | PostgreSQL 17.11;/usr/lib/postgresql/17/lib |
| 服务端 SELECT … FROM pg_config 视图 | VERSION 17.12;PKGLIBDIR /workspace/labs/db-20261113/postgresql-17/usr/lib/postgresql/17/lib |
| SHOW dynamic_library_path | $libdir(默认值) |
两个目录不是同一个——如果照命令行的结果去放 .so,服务端照样找不到。以服务端视图为准(默认只有超级用户可读):
SELECT name, setting FROM pg_config
WHERE name IN ('VERSION', 'PKGLIBDIR', 'SHAREDIR');
SHOW dynamic_library_path;ls <PKGLIBDIR> | grep -E 'pg_trgm|你的扩展名'
ldd <PKGLIBDIR>/pg_trgm.so | grep 'not found' # 文件在但依赖的系统库缺shared_preload_libraries 写错:不是报错,是服务起不来
预加载库在服务启动时就载入,找不到文件不会留给 SQL 去报,而是整个实例退出。我在 17.12 测试库执行 ALTER SYSTEM SET shared_preload_libraries = 'pg_stat_statement'(少了一个 s),ALTER SYSTEM 本身照样成功,重启时才出事:pg_ctl start 返回 rc=1,约 114 ms 退出,日志是:
FATAL: could not access file "pg_stat_statement": No such file or directory
LOG: database system is shut down服务起不来就没法再用 ALTER SYSTEM 改回去。我的做法是直接编辑数据目录里的 postgresql.auto.conf,删掉那一行(或改成正确拼写),再 pg_ctl start,110 ms 起来。如果值写在 postgresql.conf,就改那个文件。改之前先备份这个文件。
反过来,扩展需要预加载却没配,报的是另一句:pg_stat_statements must be loaded via "shared_preload_libraries"(55000),而且 CREATE EXTENSION pg_stat_statements 本身会成功,查视图时才报——见 extension is not available 页。
把 16 版的 .so 拷进 17:报错换了,问题没解决
升级后缺库,最顺手的「修法」是把旧版本目录里的 .so 拷过来。我把 PG 16.16 的几个 contrib 库直接拿给 17.12 去 LOAD:
| 拿给 17.12 的文件 | 报错原文 | SQLSTATE |
|---|---|---|
| 16 版 adminpack.so | incompatible library "…/adminpack16.so": version mismatch;DETAIL: Server is version 17, library is version 16. | XX000 |
| 16 版 pg_trgm.so | could not load library "…/pg_trgm16.so": …: undefined symbol: pg_popcount | XX000 |
| 一个不是 ELF 的文件 | could not load library "…/notelf.so": …: file too short | XX000 |
| 权限 000 的 .so | could not load library "…/noperm.so": …: cannot open shared object file: Permission denied | 42501 |
| 一个目录 | could not load library "…/badlibs": …: cannot read file data: Is a directory | 42809 |
| 库在,但函数名写错(AS '$libdir/pg_trgm', 'inch_no_such_symbol') | could not find function "inch_no_such_symbol" in file "…/pg_trgm.so" | 42883 |
也就是说,58P01 说明文件不在;XX000 说明文件在但不是给这个版本编译的。看到后者别再换着拷了,去装目标大版本的扩展包。
pg_upgrade 16→17:--check 报 loadable libraries missing 怎么读、怎么修
我在 16.16 旧集群建了自造库 shop,装 pg_trgm、adminpack、old_snapshot,再建一个 C 函数 inch_lev 指向 $libdir/inch16only(模拟「只给旧版本装过」的第三方库),然后对 17.12 新集群跑 pg_upgrade --check。620 ms 后失败,原文:
Checking for presence of required libraries fatal
Your installation references loadable libraries that are missing from the
new installation. You can add these libraries to the new installation,
or remove the functions using them from the old installation. A list of
problem libraries is in the file:
…/new17data/pg_upgrade_output.d/20261113T114446.087/loadable_libraries.txtcould not load library "$libdir/adminpack": ERROR: could not access file "$libdir/adminpack": No such file or directory
In database: shop
could not load library "$libdir/inch16only": ERROR: could not access file "$libdir/inch16only": No such file or directory
In database: shop
could not load library "$libdir/old_snapshot": ERROR: could not access file "$libdir/old_snapshot": No such file or directory
In database: shop- adminpack、old_snapshot 是 PG17 已移除的 contrib(17 的发布说明写明移除 adminpack;old_snapshot 依赖的 old_snapshot_threshold 也在 17 移除,17 源码 contrib 目录里已没有它)。17 没有对应包可装,只能在旧集群
DROP EXTENSION adminpack; DROP EXTENSION old_snapshot;(33 ms)。 - 自造的 inch16only:我先试了把 16 版的 .so 拷进 17 的库目录,再跑 --check,仍然 fatal(640 ms),文件里的原因变成
undefined symbol: pg_utf_mblen——--check 会真的去加载,拷文件骗不过它。 - 换成 17 版构建的库后 --check 通过(630 ms,
*Clusters are compatible*),pg_upgrade --copy实升 1870 ms(测试库很小,时长不代表你的库),新集群里inch_lev()返回 1、pg_trgm 1.6 的similarity()返回 0.78571427。
未测:真实 PostGIS / TimescaleDB 等第三方包的 17 版安装、pg_upgrade --link 模式、Windows 与 RPM 的目录布局、Docker 官方镜像;也没有触发 pg_upgrade 生成 update_extensions.sql 的场景(本次 16/17 的 pg_trgm 都是 1.6)。这些情况请以对应扩展和发行版的文档为准。
升级前清单:每个库跑一遍 pg_proc.probin,对着新版本库目录核对
pg_proc 是每个数据库各自一份,loadable_libraries.txt 也是按库列的(In database: shop)。所以清单要在每个库里跑,而不只是在 postgres 库里跑一次:
SELECT probin, count(*) AS funcs
FROM pg_proc
WHERE probin IS NOT NULL
GROUP BY probin
ORDER BY 1;测试库 16.16 的 shop 库,去掉内置的编码转换库(*_and_*)、plpgsql、dict_snowball 后剩 5 行:$libdir/adminpack 5 个函数、$libdir/euc2004_sjis2004 2、$libdir/inch16only 1、$libdir/old_snapshot 1、$libdir/pg_trgm 31。拿这张表去新版本的 PKGLIBDIR 里逐个 ls,缺哪个、是不是 17 已移除的,升级前就一清二楚。
我的判断:这张表比 pg_upgrade --check 更早、更全——--check 要等新集群 initdb 好才能跑,而且遇到第一个 fatal 类别就退出;清单在旧库上随时能跑,也能顺带发现不属于任何扩展、直接写死路径的自定义 C 函数(probin NOT LIKE '$libdir/%')。
修复顺序(生产库)
- 看报错里的路径:
$libdir/xxx或裸名 → 去服务端pg_config视图的 PKGLIBDIR 找xxx.so;绝对路径 → 直接检查那个路径。 - 确认是哪套安装:服务端视图的 VERSION 和 PKGLIBDIR,别用命令行 pg_config 代替。
- 缺文件 → 用操作系统包管理器装与服务端大版本一致的扩展包;装好后新连接即可用,不必重启(预加载的库除外)。
- 扩展已不再需要 →
DROP EXTENSION。测试库里 .so 缺失时 DROP 也能执行(31 ms);有依赖索引时报2BP01 cannot drop extension pg_trgm because other objects depend on it,先 DROP INDEX 或评估后 CASCADE。 - 服务起不来(预加载写错)→ 改 postgresql.auto.conf / postgresql.conf 里的 shared_preload_libraries 后再启动。
- 验收:新开连接调用一次扩展的 C 函数、跑一条走它索引的查询;不要只看 ALTER EXTENSION UPDATE 是否成功。重装扩展后要重建依赖它的索引(测试库重装 pg_trgm + 重建 2 万行 GIN 索引 87 ms)。
常见问题
could not access file "$libdir/xxx": No such file or directory 是什么意思?
SQLSTATE 58P01:正在运行的 PostgreSQL 在自己的包库目录($libdir)里找不到 xxx.so。以服务端 SELECT * FROM pg_config 里的 PKGLIBDIR 为准去找,缺了就装与服务端大版本一致的扩展包。
为什么有的连接正常、有的连接报这个错?
已经载入过这个库的老连接会继续用内存里的副本,新连接要重新加载文件才报错。测试库里 .so 被改名后,同一会话的 similarity() 照常返回,新会话立即报 58P01。
ALTER EXTENSION UPDATE 成功了,为什么还报 58P01?
升级脚本不一定加载 .so。测试库 pg_trgm 1.5→1.6 的脚本只改操作符族,.so 缺失时 UPDATE 照样成功。要用新连接实际调用一次扩展函数来验收。
pg_upgrade --check 报 loadable libraries that are missing 怎么办?
打开提示里的 loadable_libraries.txt,逐条看库名和 In database。新版本有对应包就装 17 版构建;像 adminpack、old_snapshot 这种 PG17 已移除的,在旧集群 DROP EXTENSION。拷旧版 .so 过去在测试库里仍 fatal(undefined symbol)。
shared_preload_libraries 写错了服务起不来怎么办?
日志会是 FATAL: could not access file "名字"。服务没起来不能用 ALTER SYSTEM,直接编辑数据目录里的 postgresql.auto.conf(或 postgresql.conf)改掉那一行再启动。
下一步
价格以对应页面当前标价为准。
出处(最后核对 2026-11-13)
- PostgreSQL 源码 REL_17_STABLE:src/backend/utils/fmgr/dfmgr.c(internal_load_library)
- PostgreSQL 17 文档 36.10:C-Language Functions(Dynamic Loading,$libdir)
- PostgreSQL 17 文档 19.11:Client Connection Defaults(dynamic_library_path、shared_preload_libraries)
- PostgreSQL 17 文档 52.5:pg_config 视图
- PostgreSQL 17 文档:pg_upgrade(Install extension shared object files)
- PostgreSQL 源码 REL_17_STABLE:src/bin/pg_upgrade/function.c(loadable libraries missing)
- PostgreSQL 17 Release Notes(Remove adminpack contrib extension;Remove old_snapshot_threshold)
- PostgreSQL 17 文档:Appendix A. PostgreSQL Error Codes
- PostgreSQL 源码 REL_17_STABLE:src/backend/utils/error/elog.c(errcode_for_file_access:ENOENT→58P01、EACCES→42501、EISDIR→42809)
本页摘录并解读上面列出的官方文档,不是官方页面;官方文档更新后以官方为准。除标明「盈尺实测」的一段外,其余内容都来自官方文档。