# Known Issues

This page lists known user-visible limitations in pgGraph 1.2. See
[Release Notes](./release-notes) for resolved issues and
[Roadmap](./roadmap) for planned expansion.

## Operations

### External scheduling is required outside the packaged Docker setup

pgGraph exposes bounded, concurrency-safe maintenance entry points, but it does
not run an always-on scheduler inside every PostgreSQL installation. The
packaged Docker image configures `pg_cron`. Other installations must schedule
`graph.run_scheduled_maintenance()` with `pg_cron` or an external scheduler.

Without a scheduler, reads remain available, but pending trigger-sync work,
overlay compaction, and cleanup do not run automatically. Follow the setup,
health, retry, and failure guidance in
[Sync And Maintenance](/user_guide/sync-and-maintenance#scheduled-maintenance).

## Access Control

### RLS protects results, not every timing or resource side channel

pgGraph 1.2 excludes caller-hidden node and relationship identities before
they can affect observable topology. The resource governor may still charge
projection entries examined while constructing or applying that visibility
boundary. The supported contract is PostgreSQL-like result and topology
visibility; pgGraph does not claim formal timing or resource-exhaustion
noninterference between roles with different policies.

### Some RLS workloads still use eager visibility preparation

pgGraph 1.2 uses bounded lazy visibility resolution for supported targeted
identity, traversal, path, and GQL/Cypher expansion work. Whole-graph analytics,
wide source scans, all-possible-path enumeration, and other global work retain
the eager PostgreSQL visibility oracle. Large policy-scoped graphs can therefore
spend work proportional to the visible source set and may reach
`statement_timeout` even when the final result is small.

The eager route is a correctness-preserving fallback, not an authorization
bypass. Use bounded targeted queries where possible, keep PostgreSQL source-key
indexes current, and set an explicit `statement_timeout`. Completion of the
retained 1M/10M comparison and the remaining targeted-path evidence is tracked
in the [selective-RLS roadmap section](/roadmap#post-1-2-focus-complete-selective-rls-scale-evidence).

### Tenant scope requires the session-setting path under enforcement

For a graph whose tables are registered with `tenant_column` rather than
pinned to a single graph-level tenant, an explicit tenant argument is a
caller-supplied value, not one verified against the calling role's identity.
While `graph.enforce_tenant_scope = on` (the default), pgGraph refuses an
explicit tenant argument for such a graph and requires the session tenant
(`graph.tenant_setting`) instead. pgGraph cannot verify that a session's
tenant setting reflects who is actually connected; that trust boundary
belongs to whatever sets the session GUC before the caller's own queries run.
See [Tenant Scope](/user_guide/administration-and-security#tenant-scope) for
the full boundary and mitigation guidance.

## Query Compatibility

### GQL writes accept one parameter object per call

The 1.0 mapped GQL write profile creates, merges, updates, or deletes one node
or relationship per `graph.gql()` call. It does not expose a multi-row mutation
or `UNWIND` write surface. An adapter that calls `graph.gql()` once per item
therefore pays repeated client round trips and parse, bind, ACL, and SPI work.

For bulk ingestion in 1.0, write registered PostgreSQL source tables directly
with set-based DML, commit, and apply sync once:

```sql
BEGIN;

INSERT INTO app.entities (id, name)
VALUES
  ('n-1', 'Ada'),
  ('n-2', 'Grace')
ON CONFLICT (id) DO UPDATE
SET name = EXCLUDED.name;

COMMIT;

SELECT * FROM graph.apply_sync();
```

PostgreSQL remains authoritative for constraints, triggers, ACLs, RLS, MVCC,
and partition routing. In persisted `mutable_overlay` mode, committed edge
changes become durable projection segments after sync without a full graph
rebuild. Applications with the default pending-sync query freshness may let the
next topology read catch up instead of calling `graph.apply_sync()` explicitly.

A bounded, set-based graph mutation API is the proposed
[post-1.1 roadmap direction](/roadmap#proposed-post-1-1-focus-bounded-batched-mutations).

### GQL and openCypher support are bounded profiles

`graph.gql()` implements the generated [GQL 1.0 profile](/user_guide/gql-profile),
not full ISO GQL. `graph.cypher()` accepts only the overlapping compatibility
profile and does not claim full openCypher support. Unsupported constructs fail
with a stable SQLSTATE and actionable guidance rather than being interpreted
with approximate semantics.

SQL/PGQ `GRAPH_TABLE` is not part of the PostgreSQL 14–18 contract. PostgreSQL
19 integration remains on the [Roadmap](./roadmap).

## Resource Scope

### Limits are per operation and backend, not a cluster-wide memory quota

Build, load, query, synchronization, compaction, and supported analytics enforce
their documented byte, row, work, disk, and elapsed-time policies. PostgreSQL's
own buffer cache, executor memory, and other sessions remain governed by
PostgreSQL configuration and operating-system controls. `graph.memory_limit_mb`
therefore does not replace cluster-wide capacity planning.

Use `graph.resource_status()`, `graph.build_resource_status()`, PostgreSQL
`statement_timeout`, and the production-shaped RSS/PSS release evidence when
sizing concurrent workloads. See [Configuration](/user_guide/configuration)
and [Troubleshooting](/user_guide/troubleshooting#query-or-maintenance-hits-a-resource-limit).

## Reporting A New Issue

Include the pgGraph version, PostgreSQL major, platform, exact SQLSTATE and
`PGxxx` diagnostic, relevant `graph.status()`/`graph.resource_status()` output,
and a minimal source schema. Do not include credentials or production data.
