lfnovo/open-notebook · error · InvalidInputError

Cannot delete object without an ID

Error message

Cannot delete object without an ID

What it means

delete() refuses to operate on a model instance whose id is None, raising InvalidInputError. An unsaved (never persisted) object has no SurrealDB record id, so there is nothing to delete — the guard prevents meaningless repo_delete calls.

Source

Thrown at open_notebook/domain/base.py:207

            raise
        except RuntimeError:
            # Transaction conflicts should propagate for retry
            raise
        except Exception as e:
            logger.error(f"Error saving record: {e}")
            raise DatabaseOperationError(e)

    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(

View on GitHub (pinned to a7de90d38a)

Solutions

  1. Ensure save() succeeded and the instance has an id before calling delete()
  2. Guard with: if obj.id is None: skip or raise a 404-style result
  3. If delete-after-failed-save, handle the save error first rather than proceeding

Example fix

# before
note = Note(title="x")
await note.save()  # may have failed
await note.delete()

# after
note = Note(title="x")
await note.save()
if note.id:
    await note.delete()
Defensive patterns

Strategy: type-guard

Validate before calling

if note.id is None:
    # nothing persisted yet; nothing to delete
    return

Type guard

from open_notebook.domain.base import ObjectModel

def is_persisted(obj: ObjectModel) -> bool:
    return obj.id is not None

Prevention

When it happens

Trigger: obj = Note(...); await obj.delete() before ever calling save(); re-instantiating a model from partial data (no id field) and calling delete; deleting after a failed save left id unset.

Common situations: Delete-immediately-after-create flows where save failed silently; frontend delete button firing before the object finished persisting; test fixtures building models without ids.

Related errors


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