lfnovo/open-notebook · critical · DatabaseOperationError

Failed to delete {self.__class__.table_name}

Error message

Failed to delete {self.__class__.table_name}

What it means

delete() wraps repo_delete in try/except and converts any failure into DatabaseOperationError('Failed to delete {table}'). The original error is logged (with the record id) but not attached to the raised message, so logs are the place to find the root cause.

Source

Thrown at open_notebook/domain/base.py:215

    def _prepare_save_data(self) -> Dict[str, Any]:
        data = self.model_dump()
        return {
            key: value
            for key, value in data.items()
            if value is not None or key in self.__class__.nullable_fields
        }

    async def delete(self) -> bool:
        if self.id is None:
            raise InvalidInputError("Cannot delete object without an ID")
        try:
            logger.debug(f"Deleting record with id {self.id}")
            return await repo_delete(self.id)
        except Exception as e:
            logger.error(
                f"Error deleting {self.__class__.table_name} with id {self.id}: {str(e)}"
            )
            raise DatabaseOperationError(
                f"Failed to delete {self.__class__.table_name}"
            )

    async def relate(
        self, relationship: str, target_id: str, data: Optional[Dict] = {}
    ) -> Any:
        if not relationship or not target_id or not self.id:
            raise InvalidInputError("Relationship and target ID must be provided")
        try:
            return await repo_relate(
                source=self.id, relationship=relationship, target=target_id, data=data
            )
        except Exception as e:
            logger.error(f"Error creating relationship: {str(e)}")
            logger.exception(e)
            raise DatabaseOperationError(e)

    @field_validator("created", "updated", mode="before")

View on GitHub (pinned to a7de90d38a)

Solutions

  1. Check the API logs for the 'Error deleting ...' line to get the underlying cause
  2. Verify SurrealDB connectivity and retry — transient failures often clear
  3. Catch DatabaseOperationError in the endpoint and return 503 with a retry hint to the client
  4. If 'already gone' races are common, treat subsequent NotFoundError on re-check as success (idempotent delete)

Example fix

# before
await note.delete()  # unhandled

# after
from open_notebook.domain.errors import DatabaseOperationError
try:
    await note.delete()
except DatabaseOperationError:
    raise HTTPException(status_code=503, detail="Delete failed, retry shortly")
Defensive patterns

Strategy: retry

Try / catch

from open_notebook.domain.errors import DatabaseOperationError, NotFoundError
try:
    await note.delete()
except DatabaseOperationError:
    try:
        await Note.get(note.id)  # idempotency check
    except NotFoundError:
        pass  # already deleted; treat as success
    else:
        raise HTTPException(status_code=503, detail="Delete failed, retry")

Prevention

When it happens

Trigger: SurrealDB unreachable during delete, record already deleted server-side causing an error path in the driver, permission failures, or graph-relate edges blocking removal. Logged line 'Error deleting {table} with id ...' carries the details.

Common situations: DB tier down when a user hits delete; network blip between API and SurrealDB; deletion racing with another process that already removed the record.

Related errors


AI-assisted analysis of lfnovo/open-notebook@a7de90d38a (2026-08-27). Data as JSON: /api/errors/188b312d9d70c676. Report an issue: GitHub.