{"record":{"id":"40420920f372e953","repo":"pathwaycom/pathway","slug":"sslrootcert-is-not-readable-e","errorCode":null,"errorMessage":"sslrootcert is not readable: {e}","messagePattern":"sslrootcert is not readable: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/pathway/io/postgres/__init__.py","lineNumber":242,"sourceCode":"    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\":\n        if publication_name is not None:\n            raise ValueError(\"'publication_name' is not needed for the static mode\")\n        if replication_slot_name is not None:\n            raise ValueError(","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/pathwaycom/pathway/blob/fa2f74a4649b7c5908690cf60137263d8d80de5f/python/pathway/io/postgres/__init__.py#L224-L260","documentation":"Raised by the postgres connector's _build_tls_settings when the sslrootcert file exists but cannot be opened due to an OSError other than 'is a directory' or 'not found' — in practice almost always a permissions problem. The original OS error text is appended.","triggerScenarios":"The process user lacks read permission on the CA file (mode 600 owned by root, pipeline runs as non-root); SELinux/AppArmor denying access; path component with no execute (traverse) permission.","commonSituations":"Secrets mounted as root-only files in Kubernetes; files copied with restrictive umask; systemd services running as a dedicated user.","solutions":["Grant read access: chmod 644 /path/ca.pem (or chown to the service user).","Ensure every directory in the path is traversable (chmod +x on parents).","If SELinux is enforcing, restore the correct context: restorecon -v /path/ca.pem."],"exampleFix":"# shell\n# before: ls -l /etc/pathway/root.pem -> -rw------- root root\nsudo chmod 644 /etc/pathway/root.pem\n# after: pipeline can open the file","handlingStrategy":"validation","validationCode":"import os\np = postgres_settings.get(\"sslrootcert\")\nif p is not None:\n    with open(p, \"rb\") as f:  # raises early with the OS error if unreadable\n        pass","typeGuard":null,"tryCatchPattern":"try:\n    pw.io.postgres.write(t, parts, \"tbl\", postgres_settings=pg_settings)\nexcept ValueError as e:\n    if \"sslrootcert is not readable\" in str(e):\n        logging.error(\"fix permissions on %s\", pg_settings[\"sslrootcert\"])\n        raise","preventionTips":["Mount CA files world-readable (defaultMode: 0644) in Kubernetes secrets.","Run the pipeline user's preflight as the same OS user that will execute the pipeline."],"tags":["postgres","tls","ssl","permissions","configuration"],"backgroundTag":null,"analyzedSha":"fa2f74a4649b7c5908690cf60137263d8d80de5f","analyzedAt":"2026-08-15T01:48:17.006Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}