Skip to content

Fix: Robust error handling for async database operations in graph storage#2356

Merged
danielaskdd merged 2 commits intoHKUDS:mainfrom
danielaskdd:improve-error-handling
Nov 14, 2025
Merged

Fix: Robust error handling for async database operations in graph storage#2356
danielaskdd merged 2 commits intoHKUDS:mainfrom
danielaskdd:improve-error-handling

Conversation

@danielaskdd
Copy link
Collaborator

🐛 Fix: Robust error handling for async database operations in Neo4j and Memgraph storage

Summary

This PR enhances error handling in graph storage implementations by adding defensive null checks before consuming async database results in exception handlers. This prevents secondary exceptions from masking the original error and improves overall system stability.

Problem

In several async methods across neo4j_impl.py and memgraph_impl.py, the exception handlers attempted to consume database results without checking if the result variable was successfully initialized. If an exception occurred during session.run(), the result variable would be unbound, leading to UnboundLocalError or NameError in the exception handler, which would mask the actual error.

Example of problematic code:

try:
    result = await session.run(query, ...)  # Could fail here
    # ... process result
except Exception as e:
    logger.error(f"Error: {str(e)}")
    await result.consume()  # ❌ UnboundLocalError if session.run() failed
    raise

Solution

Applied a consistent defensive programming pattern across all affected methods:

result = None  # Initialize before try block
try:
    result = await session.run(query, ...)
    # ... process result
    await result.consume()
except Exception as e:
    logger.error(f"Error: {str(e)}")
    if result is not None:  # ✅ Defensive check
        await result.consume()
    raise

Changes

Modified Files

  • lightrag/kg/neo4j_impl.py - 4 methods fixed
  • lightrag/kg/memgraph_impl.py - 7 methods fixed

Fixed Methods

Neo4j Implementation:

  • has_node() - Check node existence
  • has_edge() - Check edge existence
  • get_popular_labels() - Retrieve popular entity labels
  • get_node_edges() - Retrieve node relationships

Memgraph Implementation:

  • has_node() - Check node existence
  • has_edge() - Check edge existence
  • get_all_labels() - Retrieve all labels
  • get_node_edges() - Retrieve node relationships
  • get_edge() - Retrieve edge properties
  • get_popular_labels() - Retrieve popular entity labels
  • search_labels() - Search labels with fuzzy matching

Technical Details

Pattern Applied:

  1. Initialize result = None before the try block
  2. Assign result within try block from session.run()
  3. Add if result is not None: check in exception handler before consuming

Benefits:

  • ✅ Prevents secondary exceptions from masking original errors
  • ✅ Improves error tracking and debugging capabilities
  • ✅ Follows async resource cleanup best practices
  • ✅ Aligns with project's "Async Pattern Consistency" guidelines in .clinerules

Testing

  • ✅ Code review completed
  • ✅ Pattern consistently applied across all affected methods
  • ✅ Maintains backward compatibility - no API changes

Impact

  • Scope: Graph storage layer (Neo4j and Memgraph implementations)
  • Risk: Low - defensive improvement only, no functional changes
  • Performance: No performance impact
  • Breaking Changes: None

- Initialize result vars to None
- Add null checks before consume calls
- Prevent crashes in except blocks
- Apply fix to both Neo4J and Memgraph
@danielaskdd
Copy link
Collaborator Author

@codex review

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 1053 to 1057
workspace_label = self._get_workspace_label()
async with self._driver.session(
database=self._DATABASE, default_access_mode="READ"
) as session:
result = None

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore outer try/except in Memgraph popular label lookup

get_popular_labels() previously wrapped _get_workspace_label() and the session acquisition in a try/except that logged failures and returned an empty list. The new implementation performs those steps outside the try block, so a driver connection error or _get_workspace_label() failure now propagates as an unhandled exception and will bubble up to callers instead of returning [] as before. This is a behavioral regression and contradicts the commit’s intent of a purely defensive change.

Useful? React with 👍 / 👎.

Comment on lines 1106 to 1111
workspace_label = self._get_workspace_label()
async with self._driver.session(
database=self._DATABASE, default_access_mode="READ"
) as session:
result = None
try:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Maintain graceful failure in Memgraph label search

search_labels() likewise moved _get_workspace_label() and the session context creation outside its try/except. When Memgraph is unreachable or the helper raises, the method now bubbles the exception instead of logging and returning an empty list as it did before, causing callers to receive hard failures rather than a safe fallback.

Useful? React with 👍 / 👎.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 1054 to 1084
f"[{self.workspace}] Retrieved {len(labels)} popular labels (limit: {limit})"
)
return labels
except Exception as e:
logger.error(f"[{self.workspace}] Error getting popular labels: {str(e)}")
return []
except Exception as e:
logger.error(
f"[{self.workspace}] Error getting popular labels: {str(e)}"
)
if result is not None:
await result.consume()
return []

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve empty-list fallback when session acquisition fails

The previous version wrapped the entire method in a try/except and returned [] on any error. By moving the try block inside the async with context, failures raised while opening the Memgraph session (e.g. network outage or authentication error) now bypass the handler and bubble up to callers. That changes the public behaviour from “log and return an empty list” to propagating exceptions, which can crash code paths that previously tolerated a down graph store. Consider restoring the broader try/except so that session acquisition errors are still logged and handled consistently.

Useful? React with 👍 / 👎.

Comment on lines 1109 to 1141
f"[{self.workspace}] Search query '{query}' returned {len(labels)} results (limit: {limit})"
)
return labels
except Exception as e:
logger.error(f"[{self.workspace}] Error searching labels: {str(e)}")
return []
except Exception as e:
logger.error(f"[{self.workspace}] Error searching labels: {str(e)}")
if result is not None:
await result.consume()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Search labels now throws on connection errors instead of returning []

Similar to get_popular_labels, the try/except is now nested inside the session block, so any exception while acquiring the Memgraph session (or resolving the workspace label) is no longer caught and the method raises instead of returning [] as it did before. Callers relying on the graceful fallback will now see unhandled exceptions when the database is unavailable. Wrapping the session creation in the same try/except would keep the previous contract.

Useful? React with 👍 / 👎.

@danielaskdd
Copy link
Collaborator Author

@codex review

@chatgpt-codex-connector
Copy link

Codex Review: Didn't find any major issues. Another round soon, please!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@danielaskdd danielaskdd merged commit 399a23c into HKUDS:main Nov 14, 2025
1 check passed
@danielaskdd danielaskdd deleted the improve-error-handling branch November 14, 2025 03:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant