Skip to content

Conversation

@jaegeral
Copy link
Collaborator

This PR fixes a critical crash (sqlalchemy.exc.InvalidRequestError) that occurs when force-deleting a
sketch that contains multiple timelines sharing the same underlying search index.

@jaegeral
Copy link
Collaborator Author

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request addresses a critical crash that occurs when deleting a sketch with timelines that share the same search index. The fix involves iterating over a copy of the timelines to prevent modification-during-iteration errors and adds necessary checks to handle cases where timelines or their search indices might have already been deleted due to cascading database operations. The approach is sound and effectively resolves the reported issue. I've suggested a minor refactoring to combine two conditional checks, which will improve code conciseness and readability.

jaegeral and others added 3 commits January 19, 2026 15:34
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
@jaegeral jaegeral self-assigned this Jan 20, 2026
@jaegeral
Copy link
Collaborator Author

[2026-01-20 15:34:07,772] timesketch.app/ERROR Exception on /api/v1/sketches/33/ [DELETE]
  Traceback (most recent call last):
    File "/opt/venv/lib/python3.12/site-packages/sqlalchemy/engine/base.py", line 1910, in _execute_context
      self.dialect.do_execute(
    File "/opt/venv/lib/python3.12/site-packages/sqlalchemy/engine/default.py", line 736, in do_execute
      cursor.execute(statement, parameters)
  psycopg2.errors.ForeignKeyViolation: update or delete on table "searchindex" violates foreign key constraint "timeline_searchindex_id_fkey" on table "timeline"
  DETAIL:  Key (id)=(21) is still referenced from table "timeline".
  
  
  The above exception was the direct cause of the following exception:
  
  Traceback (most recent call last):
    File "/opt/venv/lib/python3.12/site-packages/flask/app.py", line 880, in full_dispatch_request
      rv = self.dispatch_request()
           ^^^^^^^^^^^^^^^^^^^^^^^
    File "/opt/venv/lib/python3.12/site-packages/flask/app.py", line 865, in dispatch_request
      return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)  # type: ignore[no-any-return]
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "/opt/venv/lib/python3.12/site-packages/flask_restful/__init__.py", line 489, in wrapper
      resp = resource(*args, **kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^
    File "/opt/venv/lib/python3.12/site-packages/flask/views.py", line 110, in view
      return current_app.ensure_sync(self.dispatch_request)(**kwargs)  # type: ignore[no-any-return]
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "/opt/venv/lib/python3.12/site-packages/flask_restful/__init__.py", line 604, in dispatch_request
      resp = meth(*args, **kwargs)
             ^^^^^^^^^^^^^^^^^^^^^
    File "/opt/venv/lib/python3.12/site-packages/flask_login/utils.py", line 290, in decorated_view
      return current_app.ensure_sync(func)(*args, **kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "/opt/venv/lib/python3.12/site-packages/timesketch/api/v1/resources/sketch.py", line 637, in delete
      timeline.set_status(status="deleted")
    File "/opt/venv/lib/python3.12/site-packages/timesketch/models/annotations.py", line 372, in set_status
      db_session.commit()
    File "<string>", line 2, in commit
    File "/opt/venv/lib/python3.12/site-packages/sqlalchemy/orm/session.py", line 1454, in commit
      self._transaction.commit(_to_root=self.future)
    File "/opt/venv/lib/python3.12/site-packages/sqlalchemy/orm/session.py", line 832, in commit
      self._prepare_impl()
    File "/opt/venv/lib/python3.12/site-packages/sqlalchemy/orm/session.py", line 811, in _prepare_impl
      self.session.flush()
    File "/opt/venv/lib/python3.12/site-packages/sqlalchemy/orm/session.py", line 3449, in flush
      self._flush(objects)
    File "/opt/venv/lib/python3.12/site-packages/sqlalchemy/orm/session.py", line 3588, in _flush
      with util.safe_reraise():
    File "/opt/venv/lib/python3.12/site-packages/sqlalchemy/util/langhelpers.py", line 70, in __exit__
      compat.raise_(
    File "/opt/venv/lib/python3.12/site-packages/sqlalchemy/util/compat.py", line 211, in raise_
      raise exception
    File "/opt/venv/lib/python3.12/site-packages/sqlalchemy/orm/session.py", line 3549, in _flush
      flush_context.execute()
    File "/opt/venv/lib/python3.12/site-packages/sqlalchemy/orm/unitofwork.py", line 456, in execute
      rec.execute(self)
    File "/opt/venv/lib/python3.12/site-packages/sqlalchemy/orm/unitofwork.py", line 667, in execute
      util.preloaded.orm_persistence.delete_obj(
    File "/opt/venv/lib/python3.12/site-packages/sqlalchemy/orm/persistence.py", line 343, in delete_obj
      _emit_delete_statements(
    File "/opt/venv/lib/python3.12/site-packages/sqlalchemy/orm/persistence.py", line 1468, in _emit_delete_statements
      c = connection._execute_20(
          ^^^^^^^^^^^^^^^^^^^^^^^
    File "/opt/venv/lib/python3.12/site-packages/sqlalchemy/engine/base.py", line 1710, in _execute_20
      return meth(self, args_10style, kwargs_10style, execution_options)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "/opt/venv/lib/python3.12/site-packages/sqlalchemy/sql/elements.py", line 334, in _execute_on_connection
      return connection._execute_clauseelement(
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "/opt/venv/lib/python3.12/site-packages/sqlalchemy/engine/base.py", line 1577, in _execute_clauseelement
      ret = self._execute_context(
            ^^^^^^^^^^^^^^^^^^^^^^
    File "/opt/venv/lib/python3.12/site-packages/sqlalchemy/engine/base.py", line 1953, in _execute_context
      self._handle_dbapi_exception(
    File "/opt/venv/lib/python3.12/site-packages/sqlalchemy/engine/base.py", line 2134, in _handle_dbapi_exception
      util.raise_(
    File "/opt/venv/lib/python3.12/site-packages/sqlalchemy/util/compat.py", line 211, in raise_
      raise exception
    File "/opt/venv/lib/python3.12/site-packages/sqlalchemy/engine/base.py", line 1910, in _execute_context
      self.dialect.do_execute(
    File "/opt/venv/lib/python3.12/site-packages/sqlalchemy/engine/default.py", line 736, in do_execute
      cursor.execute(statement, parameters)
  sqlalchemy.exc.IntegrityError: (psycopg2.errors.ForeignKeyViolation) update or delete on table "searchindex" violates foreign key constraint "timeline_searchindex_id_fkey" on table "timeline"
  DETAIL:  Key (id)=(21) is still referenced from table "timeline".
  
  [SQL: DELETE FROM searchindex WHERE searchindex.id = %(id)s]
  [parameters: {'id': 21}]
  (Background on this error at: https://sqlalche.me/e/14/gkpj)

@jaegeral
Copy link
Collaborator Author

/gemini review

@jaegeral jaegeral requested a review from jkppr January 21, 2026 15:53
@jaegeral jaegeral marked this pull request as ready for review January 21, 2026 15:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant