lfnovo/open-notebook · error · RuntimeError

Failed to update record: {str(e)}

Error message

Failed to update record: {str(e)}

What it means

repo_update() executes a SurrealQL UPDATE and wraps any exception as RuntimeError('Failed to update record: <cause>'). Unlike repo_create it embeds the cause in the message, so the text names the actual problem — commonly 'record not found', schema type mismatches, or fields not defined in the schema when the table is schemafull.

Source

Thrown at open_notebook/database/repository.py:205

        _ensure_safe_identifier(table, "table")
        if isinstance(id, RecordID):
            record_id: RecordID = id
        elif ":" in id and id.startswith(f"{table}:"):
            record_id = ensure_record_id(id)
        else:
            record_id = RecordID(table, id)
        data.pop("id", None)
        if "created" in data and isinstance(data["created"], str):
            data["created"] = datetime.fromisoformat(data["created"])
        data["updated"] = datetime.now(timezone.utc)
        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:

View on GitHub (pinned to a7de90d38a)

Solutions

  1. Read the appended cause text — it is the actual SurrealDB error
  2. If 'not found', verify the record still exists (repo_query by id) and refresh stale ids in the caller
  3. Ensure new model fields have a corresponding migration and payload values are JSON primitives (str/int/float/bool/list/dict, isoformat dates)
  4. Retry once if the failure was a transient connection issue

Example fix

// before
data = {"created_at": datetime.utcnow()}
await repo_update(record_id, data)
// after
data = {"created_at": datetime.utcnow().isoformat()}
await repo_update(record_id, data)
Defensive patterns

Strategy: try-catch

Validate before calling

existing = await repo_query("SELECT id FROM type::table($t) WHERE id = $id", {'t': table, 'id': rid})
if not existing:
    skip update  # record gone

Try / catch

try:
    await repo_update(record_id, data)
except RuntimeError as e:
    if 'not found' in str(e):
        return None  # treat as already deleted
    raise

Prevention

When it happens

Trigger: Updating a record id that no longer exists; sending None where the schema requires a type; updating a field not present in a SCHEMAFULL table definition; serializing unsupported Python objects (datetime, enums) into the data dict.

Common situations: Concurrent deletes racing an update; stale record ids held in frontend state after another client deleted the row; model changes adding fields without a migration; passing raw datetime/Enum objects in the data payload.

Related errors


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