psycopg/psycopg2 · critical · Warning

pg_config --{attr_name} is empty

Error message

pg_config --{attr_name} is empty

What it means

Raised in PostgresConfig.query() (setup.py:120) as a Warning when pg_config exited 0 but printed nothing for a required attribute (libdir, includedir, includedir-server). An empty result cannot drive the compiler/linker search paths, so the build aborts. Attributes queried with empty_ok=True (ldflags, cppflags) are exempt and will not trigger this; the required ones will.

Source

Thrown at setup.py:120

            sys.exit(1)

    def query(self, attr_name, *, empty_ok=False):
        """Spawn the pg_config executable, querying for the given config
        name, and return the printed value, sanitized. """
        try:
            pg_config_process = subprocess.run(
                [self.pg_config_exe, "--" + attr_name],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE)
        except OSError:
            raise Warning(
                f"Unable to find 'pg_config' file in '{self.pg_config_exe}'")
        if pg_config_process.returncode:
            err = pg_config_process.stderr.decode(errors='backslashreplace')
            raise Warning(f"pg_config --{attr_name} failed: {err}")
        result = pg_config_process.stdout.decode().strip()
        if not result and not empty_ok:
            raise Warning(f"pg_config --{attr_name} is empty")
        return result

    def find_on_path(self, exename, path_directories=None):
        if not path_directories:
            path_directories = os.environ['PATH'].split(os.pathsep)
        for dir_name in path_directories:
            fullpath = os.path.join(dir_name, exename)
            if os.path.isfile(fullpath):
                return fullpath
        return None

    def autodetect_pg_config_path(self):
        """Find and return the path to the pg_config executable."""
        if PLATFORM_IS_WINDOWS:
            return self.autodetect_pg_config_path_windows()
        else:
            return self.find_on_path('pg_config')

View on GitHub (pinned to 3a6d9d6ddc)

Solutions

  1. Reinstall the proper PostgreSQL dev package so pg_config emits real paths.
  2. Verify manually: '/path/to/pg_config --includedir' and '--libdir' must each print a non-empty directory.
  3. Fall back to psycopg2-binary if the local pg_config cannot be repaired.
Defensive patterns

Strategy: validation

Validate before calling

import subprocess, shutil

def pg_config_attr_is_nonempty(attr: str, path: str | None = None) -> bool:
    p = path or shutil.which('pg_config')
    if not p:
        return False
    res = subprocess.run([p, '--' + attr], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    return res.returncode == 0 and bool(res.stdout.decode().strip())

for attr in ('libdir', 'includedir', 'includedir-server'):
    assert pg_config_attr_is_nonempty(attr), f'pg_config --{attr} returned empty'

Try / catch

# Pre-build sanity check in CI:
#   for a in libdir includedir includedir-server; do
#     v="$(pg_config --$a)"; [ -n "$v" ] || { echo "empty $a"; exit 1; }
#   done

Prevention

When it happens

Trigger: During finalize_options(), pg_config_helper.query('libdir') / query('includedir') / query('includedir-server') returns an empty stdout despite exit code 0. Common with a stub or mispackaged pg_config that emits blank lines for some attributes.

Common situations: Custom/minimal container images shipping a stripped pg_config; very old or hand-built Postgres installs whose pg_config lacks certain keys; packaging bugs in third-party Postgres distributions.

Related errors


AI-assisted analysis of psycopg/psycopg2@3a6d9d6ddc (2026-08-04). Data as JSON: /data/errors/0e64f8387e9bf73a.json. Report an issue: GitHub.