Diagnosing Empty Result Sets in SurrealDB Graph Traversals
Learn how to diagnose and fix 'Record Not Found' errors and empty result sets in SurrealDB graph traversals, focusing on directionality, FETCH clauses, and permissions.
27 Oct 2025, 02:33 UTC

The Problem: The Vanishing Relation
In SurrealDB, it is common to encounter a scenario where you have successfully executed a RELATE statement, yet a subsequent graph traversal query returns an empty array [] or a list of record IDs that your application interprets as missing data. This typically happens not because the data is gone, but because of a mismatch between the graph's directional logic, the query syntax, or the security scope.
Diagnostic Quick-Reference
| Symptom | Likely Cause | Diagnostic Check |
|---|---|---|
Query returns [] despite known data |
Directional Mismatch | Swap -> for <- |
| Query returns IDs but no object data | Missing FETCH |
Add FETCH clause to query |
Query returns [] for specific users |
Permission Scope | Test with root credentials |
Query returns [] after schema change |
Schema-full Type Mismatch | Check DEFINE TABLE types |
Step-by-Step Resolution Path
1. Verify Edge Existence
Before debugging the traversal syntax, confirm the relation (the edge) actually exists in the database. A traversal is a shortcut; a direct query to the relation table is the source of truth.
Run this command in the SurrealDB CLI or via your API client:
-- Replace 'bought' with your relation table name
SELECT * FROM bought WHERE out = 'user:john' AND in = 'product:laptop';
Expected Result: If this returns an empty set, the RELATE operation failed or was never committed. If it returns a record, the issue lies in your traversal query.
2. Validate Traversal Direction
SurrealDB graph relations are directional. If you relate a user to a product using RELATE user:john -> bought -> product:laptop, the edge originates at the user and ends at the product.
- Outbound (->): Used to find what the starting node points to.
- Inbound (<-): Used to find what points to the starting node.
If you are querying from the product's perspective to find the user, you must use the inbound operator:
-- This will fail if you use ->
SELECT <-bought.out FROM product:laptop;
3. Distinguish Between IDs and Objects
A common point of confusion is the difference between a Record ID and the Record Content. By default, a graph traversal returns the ID of the related record.
Example Scenario:
SELECT ->bought.out FROM user:john; might return ['product:laptop']. If your application code expects a JSON object with a name and price, it may treat this string ID as a "missing record."
To resolve this, use the FETCH clause to expand the IDs into full objects:
-- Run this to expand the related record data
SELECT ->bought.out AS products FROM user:john FETCH products;
4. Audit Permissions and Scopes
If the query works for an administrator but returns [] for a logged-in user, the issue is likely a DEFINE ACCESS rule. In SurrealDB, to traverse a graph, the user must have READ permissions on both the node tables and the relation table.
Check if the relation table has a restrictive permission:
-- Check the current definition of the relation table
INFO TABLE bought;
If the user lacks access to the bought table, the traversal will silently filter out the results rather than throwing an access denied error.
Comparison: Traversal Methods
| Method | Syntax | Best Use Case | Risk |
|---|---|---|---|
| Direct Traversal | ->table.out |
Quick lookups of immediate neighbors. | Can be slow on massive datasets without LIMIT. |
| FETCH Expansion | ... FETCH field |
When application logic requires full object properties. | Increased payload size and memory usage. |
| Direct Table Query | SELECT * FROM table |
Debugging and verifying edge existence. | Bypasses graph logic; less intuitive for complex paths. |
Limitations and Verification
Memory Warning: Avoid executing SELECT * FROM user:john->bought.out on nodes with thousands of edges without a LIMIT clause, as this can lead to memory exhaustion in the SurrealDB instance.
Verification Check: To confirm the fix, execute the query with FETCH and a LIMIT 1. If the result returns a full object instead of an empty array or a raw ID, the traversal path and permissions are correctly aligned.
Rollback Procedure
If you modified table permissions to debug this issue, revert them using the original DEFINE ACCESS statement to ensure security is not compromised in production.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.