{"record":{"id":"7c9158c7d37fbaa7","repo":"pathwaycom/pathway","slug":"sslrootcert-doesn-t-point-to-a-file","errorCode":null,"errorMessage":"sslrootcert doesn't point to a file","messagePattern":"sslrootcert doesn't point to a file","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/pathway/io/postgres/__init__.py","lineNumber":238,"sourceCode":"        path = \"/\"\n    else:\n        path = \"\"\n\n    owned_settings[\"replication\"] = \"database\"\n    query = \"&\".join(f\"{enc(k)}={enc(v)}\" for (k, v) in owned_settings.items())\n\n    return f\"postgresql://{userinfo}{hostport}{path}?{query}\"\n\n\ndef _build_tls_settings(owned_postgres_settings: dict) -> TLSSettings:\n    sslmode = owned_postgres_settings.pop(\"sslmode\", \"prefer\")\n\n    sslrootcert = owned_postgres_settings.pop(\"sslrootcert\", None)\n    if sslrootcert is not None:\n        try:\n            open(sslrootcert).close()\n        except IsADirectoryError as e:\n            raise ValueError(\"sslrootcert doesn't point to a file\") from e\n        except FileNotFoundError as e:\n            raise ValueError(\"sslrootcert points to a non-existent path\") from e\n        except OSError as e:\n            raise ValueError(f\"sslrootcert is not readable: {e}\") from e\n\n    return TLSSettings(mode=sslmode, root_cert_path=sslrootcert)\n\n\ndef _construct_replication_settings(\n    *,\n    mode: Literal[\"streaming\", \"static\"],\n    postgres_settings: dict,\n    publication_name: str | None,\n    replication_slot_name: str | None,\n    snapshot_name: str | None,\n):\n    # static mode doesn't require replication slots\n    if mode == \"static\":","sourceCodeStart":220,"sourceCodeEnd":256,"githubUrl":"https://github.com/pathwaycom/pathway/blob/fa2f74a4649b7c5908690cf60137263d8d80de5f/python/pathway/io/postgres/__init__.py#L220-L256","documentation":"Raised by Pathway's postgres connector (_build_tls_settings) when the sslrootcert setting in postgres_settings points to a directory instead of a regular file. The connector opens the path to validate it before building TLSSettings, and Python raises IsADirectoryError, which is converted to this ValueError.","triggerScenarios":"Passing postgres_settings={\"sslrootcert\": \"/etc/ssl/certs\"} (a directory) to pw.io.postgres.read/write; passing a path whose trailing slash makes it resolve to a directory.","commonSituations":"Users point sslrootcert at the system CA bundle directory (as libpq sometimes accepts) instead of a single PEM file like /etc/ssl/cert.pem; copy-pasting sslmode/sslrootcert examples from psql configs.","solutions":["Point sslrootcert at a single CA certificate file in PEM format, e.g. /etc/ssl/certs/ca-certificates.crt.","Concatenate the needed CAs into one file (cat bundle/*.pem > ca.pem) and use that path.","If you only wanted sslmode, remove sslrootcert entirely."],"exampleFix":"# before\npw.io.postgres.write(t, parts, \"tbl\", postgres_settings={\"sslmode\": \"verify-full\", \"sslrootcert\": \"/etc/ssl/certs\"})\n# after\npw.io.postgres.write(t, parts, \"tbl\", postgres_settings={\"sslmode\": \"verify-full\", \"sslrootcert\": \"/etc/ssl/certs/ca-certificates.crt\"})","handlingStrategy":"validation","validationCode":"import os\np = postgres_settings.get(\"sslrootcert\")\nif p is not None:\n    assert os.path.isfile(p), f\"sslrootcert must be a file, got directory: {p}\"","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Remember sslrootcert takes one PEM file, not a CA directory (unlike some libpq/openssl tooling).","Validate all TLS file settings in one place before building the pipeline."],"tags":["postgres","tls","ssl","configuration"],"backgroundTag":null,"analyzedSha":"fa2f74a4649b7c5908690cf60137263d8d80de5f","analyzedAt":"2026-08-15T01:48:17.006Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}