lfnovo/open-notebook · error · RuntimeError
Failed to delete record: {str(e)}
Error message
Failed to delete record: {str(e)} What it means
repo_delete() calls connection.delete(ensure_record_id(record_id)) and wraps any failure as RuntimeError('Failed to delete record: <cause>'). Typical causes: malformed record id string, the record not existing, or connection/permission errors. The original exception is logged via logger.exception before re-raising.
Source
Thrown at open_notebook/database/repository.py:216
query = "UPDATE $target MERGE $data;"
# logger.debug(f"Update query: {query}")
result = await repo_query(query, {"target": record_id, "data": data})
# if isinstance(result, list):
# return [_return_data(item) for item in result]
return parse_record_ids(result)
except Exception as e:
raise RuntimeError(f"Failed to update record: {str(e)}")
async def repo_delete(record_id: Union[str, RecordID]):
"""Delete a record by record id"""
try:
async with db_connection() as connection:
return await connection.delete(ensure_record_id(record_id))
except Exception as e:
logger.exception(e)
raise RuntimeError(f"Failed to delete record: {str(e)}")
async def repo_insert(
table: str, data: List[Dict[str, Any]], ignore_duplicates: bool = False
) -> List[Dict[str, Any]]:
"""Create a new record in the specified table"""
try:
async with db_connection() as connection:
result = parse_record_ids(await connection.insert(table, data))
# SurrealDB may return a string error message instead of the expected records
if isinstance(result, str):
raise RuntimeError(result)
return result
except RuntimeError as e:
if ignore_duplicates and "already contains" in str(e):
return []
# Log transaction conflicts at debug level (they are expected during concurrent operations)
error_str = str(e).lower()View on GitHub (pinned to a7de90d38a)
Solutions
- Inspect the appended cause and the logged stack trace
- Validate the id with ensure_record_id semantics before calling: must be 'table:id' format
- Make delete idempotent: check existence first or tolerate 'not found' in the caller
- Prevent duplicate delete requests in the UI (disable button after click)
Example fix
// before
await repo_delete(note_id_or_none)
// after
from surrealdb import RecordID
table, _, rid = str(note_id).partition(':')
assert table and rid, 'expected table:id'
await repo_delete(RecordID(table, rid)) Defensive patterns
Strategy: try-catch
Validate before calling
from open_notebook.database.repository import ensure_record_id
try:
rid = ensure_record_id(record_id) # raises on malformed id
except Exception:
raise InvalidInputError('expected table:id record id') Type guard
import re
def is_record_id(v) -> bool:
return isinstance(v, str) and bool(re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*:[^:]+$', v)) or isinstance(v, RecordID) Try / catch
try:
await repo_delete(record_id)
except RuntimeError as e:
if 'not found' in str(e).lower():
return # idempotent delete
raise Prevention
- Make deletes idempotent — tolerate not-found
- Validate 'table:id' format client-side before sending
- Disable delete buttons after first click to avoid duplicate requests
When it happens
Trigger: Passing a plain string like 'note' or 'note:' instead of a valid 'table:id' record id; deleting an already-deleted record (some SurrealDB versions error on missing targets); connection dropped mid-delete; deleting across a namespace where the table doesn't exist.
Common situations: Double-delete from UI double-clicks or concurrent requests; frontend sending an unsanitized id; retries after a first successful delete; env pointing at the wrong namespace so nothing matches.
Related errors
- Failed to delete {self.__class__.table_name}
- Error deleting session: {str(e)}
- Failed to delete episode
- Search failed: {str(e)}
- Error deleting transformation: {str(e)}
AI-assisted analysis of lfnovo/open-notebook@a7de90d38a (2026-08-27).
Data as JSON: /api/errors/d1fad036bac39836.
Report an issue: GitHub.