# Troubleshooting Use this page to map symptoms to status checks and recovery actions. ## Function Does Not Exist for a Registration Call Symptom: ```text ERROR: function graph.add_table(unknown, unknown) does not exist HINT: No function matches the given name and argument types. ``` PostgreSQL treats a quoted table name as an `unknown` string unless you cast it to the `regclass` table-identifier type required by pgGraph registration APIs. This does not mean the extension or function is missing. Incorrect: ```sql SELECT graph.add_table('public.users', 'id'); ``` Fix: ```sql SELECT graph.add_table('public.users'::regclass, 'id'); ``` Use `::regclass` for table arguments to `graph.add_table()`, `graph.add_edge()`, traversal, path, search, and related APIs. PostgreSQL also validates that the named relation exists when it resolves the cast. ## Stale Registration Produces PG000 Symptom: ```text SQLSTATE XX000 DETAIL: pgGraph diagnostic: PG000 registered source relation OID ... no longer exists; re-register it ``` This error can occur after a registered table is dropped and recreated, or after a logical restore assigns new PostgreSQL relation OIDs. The selected graph still refers to the old relation identity. Clear the selected graph's derived state and stale registrations, then reapply the reviewed registration SQL: ```sql SELECT graph.reset(true); SELECT graph.add_table(...); SELECT graph.add_edge(...); -- Reapply graph.add_filter_column(...) calls when used. SELECT * FROM graph.build(); ``` `graph.reset(true)` does not modify PostgreSQL source tables or other named graphs. It does clear every registration for the selected graph, so reapply all required table, edge, and filter registrations before `graph.build()`. Use this recovery only when the `PG000` message identifies a stale registered relation or relation OID. For another `PG000` internal error, keep the full message and reproduction details and report the issue. ## Graph Not Built Symptom: ```text SQLSTATE 55000 DETAIL: pgGraph diagnostic: PG003 Graph not built. Call graph.build() first. ``` Check: ```sql SELECT node_count, edge_count, schema_status, invalid_reason FROM graph.status(); ``` Fix: ```sql SELECT * FROM graph.build(); ``` ## Node Not Found Symptom: ```text SQLSTATE P0002 DETAIL: pgGraph diagnostic: PG010 Node not found ``` Check: ```sql SELECT * FROM graph.search('id', 'expected_id', table_filter := 'public.my_table'::regclass, mode := 'exact'); SELECT * FROM graph.registered_tables(); ``` Likely causes: | Cause | Fix | |---|---| | Wrong table or ID string | Use exact source primary-key text | | Composite ID mismatch | Use JSON array text encoding from `graph.node_ref_string()` patterns | | Source row added after build | `graph.apply_sync()` or `graph.build()` | | Source row deleted/tombstoned | Rebuild or query another active node | ## Search Returns No Rows Check that the column is registered in `graph.add_table(..., columns := ...)`: ```sql SELECT * FROM graph.registered_tables() WHERE table_name = 'public.users'; ``` Then test exact mode and table filter: ```sql SELECT * FROM graph.search( 'name', 'Alice', table_filter := 'public.users'::regclass, mode := 'exact', hydrate := true ); ``` Search uses source-table SQL, so source table indexes matter. Add normal PostgreSQL indexes for high-volume searched columns. ## Traversal Returns Too Few Rows Check: ```sql SELECT * FROM graph.status(); SELECT * FROM graph.traverse( 'public.users'::regclass, 'u1', max_depth := 1, edge_types := NULL, direction := 'any', hydrate := false, max_nodes := 100000, max_frontier := 100000 ); ``` Likely causes: | Cause | Fix | |---|---| | `max_depth` too low | Increase `max_depth` | | `edge_types` filter excludes edges | Remove filter or inspect `edge_types` in `graph.status()` | | `direction` wrong for unidirectional edges | Use `out`, `in`, or register bidirectional edges | | `max_nodes` or `max_frontier` trips | Raise circuit breakers for trusted query | | Tenant scope excludes nodes | Pass correct `tenant` or session tenant | | Filter column not registered/rebuilt | Register filter column and rebuild | ## Unsupported Operation With Pending Overlays Symptom: ```text SQLSTATE 0A000 DETAIL: pgGraph diagnostic: PG018 Unsupported graph operation ``` Likely cause: | Cause | Fix | |---|---| | `graph.weighted_shortest_path()` ran while `edge_buffer_used > 0` | Run `graph.vacuum()` or `graph.maintenance()` to merge pending edge overlays, then retry | Unweighted traversal, unweighted shortest path, and connected components include pending edge overlays after `graph.apply_sync()`. Weighted shortest path fails closed while overlays are pending because pending edge mutations do not carry edge weights. ## RLS Relationship Identity Requires A Rebuild Symptom: ```text SQLSTATE 55000 DETAIL: pgGraph diagnostic: PG023 ``` pgGraph 1.1 evaluates relationship-source RLS before an edge can affect topology. That requires every projected edge to retain its source-row identity. If a compatible legacy artifact does not contain a usable identity for an RLS-active relationship mapping, pgGraph fails closed instead of treating the edge as visible. Rebuild from the authoritative PostgreSQL tables: ```sql SELECT * FROM graph.build(); ``` The replacement is published only after validation. If the rebuild fails or is cancelled, the previous published generation remains authoritative, but RLS queries that require the missing identity continue to fail closed until a successful rebuild. ## Recursive RLS Visibility Resolution Symptom: ```text SQLSTATE 55000 DETAIL: pgGraph diagnostic: PG024 ``` An RLS policy on a registered source table called a pgGraph topology function while pgGraph was already asking PostgreSQL to evaluate that policy. pgGraph rejects this recursion instead of re-entering traversal with partially built visibility state. Move the nested graph query out of the policy, or compute the policy predicate from ordinary PostgreSQL relations and session settings. ## Direct RLS Probe Does Not Support This Primary-Key Type Symptom: ```text SQLSTATE 0A000 DETAIL: pgGraph diagnostic: PG018 ``` RLS identity matching supports built-in boolean, integer, text, character, numeric, OID, and UUID primary-key columns. Date/time, array, domain, extension, and custom types can format the same value differently under session settings or custom I/O code. A 1.1 projection stores only the formatted identity, so pgGraph cannot safely match those types. A topology shape fails closed when it can consume the affected RLS-active mapping; direct node probes validate only their requested node mapping. Use a stable supported key column for the graph identity. `graph.rls_mode = 'legacy_bypass'` is only an authorized maintenance escape hatch and must not be used to serve tenant-isolated reads. ## Schema Invalid Or Needs Rebuild Symptoms: ```sql SELECT schema_status, needs_rebuild, invalid_reason FROM graph.status(); ``` Fix after catalog or source schema changes: ```sql SELECT * FROM graph.registered_tables(); SELECT * FROM graph.registered_edges(); SELECT * FROM graph.build(); ``` If a registered column no longer exists, remove or update the registration. ## Build Fails With Memory Error Symptom: SQLSTATE `53200`, diagnostic `PG001`. Check: ```sql SELECT * FROM graph.estimate(); SHOW graph.memory_limit_mb; ``` Fix: ```sql ALTER SYSTEM SET graph.memory_limit_mb = 8192; SELECT pg_reload_conf(); ``` Or reduce registered graph scope. ## Query Or Maintenance Hits A Resource Limit Symptom: SQLSTATE `54000`, diagnostic `PG007`. The error names the phase, resource kind, current use, requested amount, and configured limit. Inspect the most recent completed governed operation in the same backend: ```sql SELECT * FROM graph.resource_status(); SHOW graph.query_memory_mb; SHOW graph.maintenance_memory_mb; SHOW graph.spill_disk_limit_mb; SHOW graph.query_work_limit; SHOW graph.operation_timeout_ms; ``` Reduce result cardinality, depth, wildcard range, hydration width, sync batch, or compaction range before raising a limit. If you raise a memory setting, remember that `graph.memory_limit_mb` is still the hard backend-private ceiling after the loaded graph's residency. PostgreSQL cancellation and `statement_timeout` are checked throughout long graph operations. ## Build Or Vacuum Locked Symptom: SQLSTATE `55P03`, diagnostic `PG006`. Check: ```sql SELECT pid, state, query FROM pg_stat_activity WHERE query ILIKE '%graph.build%' OR query ILIKE '%graph.vacuum%' OR query ILIKE '%graph.maintenance%'; ``` Wait for the current operation or investigate the owning session. ## Corrupt Or Incompatible Artifact Symptoms: SQLSTATE `XX001`/diagnostic `PG009`, or SQLSTATE `0A000`/diagnostic `PG011`. Fix: ```sql SELECT * FROM graph.build(); ``` If needed: ```sql SELECT graph.reset(); SELECT * FROM graph.build(); ``` ## Edge Buffer Full Symptom: diagnostic `PG008` or `PG012`, `read_only = true`, or high `edge_buffer_used`. The corresponding SQLSTATE is `54000` or `55000`. Check: ```sql SELECT edge_buffer_used, needs_vacuum, read_only, read_only_reason FROM graph.status(); ``` Fix: ```sql SELECT * FROM graph.maintenance(); ``` or: ```sql SELECT * FROM graph.vacuum(); ``` ## ACL Denied Symptom: SQLSTATE `42501`, diagnostic `PG002`. Fix for read queries: ```sql GRANT SELECT ON public.source_table TO app_role; ``` Fix for admin operations: ```sql GRANT USAGE, CREATE ON SCHEMA graph TO graph_admin; ``` ## Operational Failure Matrix Use this matrix when diagnosing complex production failures relating to jobs, quotas, sync, or storage. | Failure | Stable state | SQLSTATE / diagnostic | Visible location | Recovery action | Read impact | |---|---|---|---|---|---| | **Sync falls behind** | `sync_status = 'syncing'`, `sync_lag > 0` | `0A000` / `PG018` if the lag surfaces as an unsupported-operation guard | `graph.status().sync_lag`, `graph.status().pending_sync_rows`, `graph.jobs()` | Run `graph.run_due_jobs()` or `graph.run_job(job_id)` after confirming the source tables are healthy. | Serves the last valid generation. | | **Artifacts are missing** | `graph.projection_status().artifact_validation_state = 'full_rebuild'` | `XX001` / `PG009` | `graph.status().schema_status`, `graph.projection_status().artifact_validation_state` | Run `graph.projection_repair()` or `graph.build()` to publish a fresh artifact. | Reads fail on affected paths until rebuilt. | | **Artifact validation fails** | `graph.projection_status().artifact_validation_state = 'targeted_chunk_repair'` | `XX001` / `PG009` | `graph.projection_status().artifact_validation_state` | Run `graph.projection_repair()`; if repair cannot salvage the files, rebuild. | Reads fail on affected paths until repaired. | | **Disk is full** | `read_only_reason` indicates storage pressure | `53100` | `graph.status().read_only_reason`, `graph.job_runs()` | Free space, then rerun `graph.run_due_jobs()` or `graph.projection_repair()`. | Serves the last valid generation. | | **Compaction fails** | `graph.projection_status().artifact_validation_state = 'targeted_chunk_repair'` after a failed compaction run | `0A000` / `PG018` | `graph.projection_status().last_compaction_unix_micros`, `graph.job_runs()` | Fix the underlying artifact problem and rerun `graph.projection_repair()` or `graph.maintenance()`. | Serves the last valid generation. | | **Projection ingest fails** | `pending_durable_rows > 0` and `repair_recommended = true` | `0A000` / `PG018` | `graph.projection_status().pending_durable_rows`, `graph.projection_status().repair_recommended` | Repair the projection artifact, then rerun the ingest path. | Serves the last valid generation. | | **A job repeatedly errors** | `graph.jobs().last_status = 'retryable_failed'` or `graph.jobs().last_status = 'permanent_failed'` | `XX000` or a more specific job SQLSTATE | `graph.jobs()`, `graph.job_runs()` | Inspect the most recent run error, correct the source issue, and rerun the job. | Depends on job type. | | **A graph exceeds quota** | `graph.graph_quota_usage().exceeded = true` | `54000` / `PG019` | `graph.graph_quota_usage()` | Raise the quota or shrink graph scope/storage. | New writes, builds, or loads can be blocked. | | **Background workers unavailable** | `graph.jobs().last_status = 'queued'` | N/A | `graph.jobs()`, `graph.job_runs()` | Restore worker capacity, then run `graph.run_due_jobs()` manually. | Serves the last valid generation. | | **Schema/catalog drift** | `schema_status = 'invalid'` and `needs_rebuild = true` | `0A000` / `PG018` | `graph.status().schema_status`, `graph.status().needs_rebuild` | Run `graph.build()` for the selected graph. | Reads fail on affected paths until rebuilt. | ## Maintainer Validation If a reproducible issue requires release-class ACL, concurrency, install, or crash validation, include the public diagnostics above in the issue report. Maintainers can then select the appropriate focused gate from the [Testing And Release](../contributor_guide/testing-release) guide.