pymysql.err.OperationalError: (1034, "Index for table 'tabGL Entry' is corrupt; try to repair it") P

Problem Summary
I’m facing a critical database corruption issue with my production ERPNext system and need expert advice on the best recovery approach. The corruption is preventing access to essential accounting data.

REPAIR TABLE tabGL Entry; – Doesn’t work with InnoDB
InnoDB tables can’t use REPAIR TABLE

  • Attempted to repair the table, but MySQL reports it’s InnoDB, so the repair didn’t work.
  • I created a backup to test. phpMyAdmin shows 186,316 rows in tabGL Entry
  • When I try to export and re-import the table, I end up with only ~13,000 rows.

What are the recommended steps to fix this index corruption and recover all rows from tabGL Entry?

App Versions

{
“commtrack”: “0.0.1”,
“datavalue_theme_15”: “1.0.0”,
“erpnext”: “15.28.0”,
“frappe”: “15.31.0”,
“healthcare”: “15.0.0”,
“healthcare_addon”: “0.0.2”,
“hrms”: “15.22.3”,
“sound_notifier”: “0.0.1”
}

Route

Workspaces/Accounting

Traceback

Traceback (most recent call last):
File “apps/frappe/frappe/app.py”, line 114, in application
response = frappe.api.handle(request)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File “apps/frappe/frappe/api/init.py”, line 49, in handle
data = endpoint(**arguments)
^^^^^^^^^^^^^^^^^^^^^
File “apps/frappe/frappe/api/v1.py”, line 36, in handle_rpc_call
return frappe.handler.handle()
^^^^^^^^^^^^^^^^^^^^^^^
File “apps/frappe/frappe/handler.py”, line 49, in handle
data = execute_cmd(cmd)
^^^^^^^^^^^^^^^^
File “apps/frappe/frappe/handler.py”, line 85, in execute_cmd
return frappe.call(method, **frappe.form_dict)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “apps/frappe/frappe/init.py”, line 1768, in call
return fn(*args, **newargs)
^^^^^^^^^^^^^^^^^^^^
File “apps/frappe/frappe/utils/typing_validations.py”, line 31, in wrapper
return func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File “apps/frappe/frappe/init.py”, line 921, in wrapper_fn
retval = fn(*args, **get_newargs(fn, kwargs))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “apps/frappe/frappe/desk/query_report.py”, line 223, in run
result = generate_report_result(report, filters, user, custom_columns, is_tree, parent_field)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “apps/frappe/frappe/init.py”, line 921, in wrapper_fn
retval = fn(*args, **get_newargs(fn, kwargs))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “apps/frappe/frappe/desk/query_report.py”, line 84, in generate_report_result
res = get_report_result(report, filters) or
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “apps/frappe/frappe/desk/query_report.py”, line 65, in get_report_result
res = report.execute_script_report(filters)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “apps/frappe/frappe/core/doctype/report/report.py”, line 162, in execute_script_report
res = self.execute_module(filters)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “apps/frappe/frappe/core/doctype/report/report.py”, line 179, in execute_module
return frappe.get_attr(method_name)(frappe._dict(filters))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “apps/erpnext/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py”, line 28, in execute
income = get_data(
^^^^^^^^^
File “apps/erpnext/erpnext/accounts/report/financial_statements.py”, line 180, in get_data
set_gl_entries_by_account(
File “apps/erpnext/erpnext/accounts/report/financial_statements.py”, line 483, in set_gl_entries_by_account
gl_entries += get_accounting_entries(
^^^^^^^^^^^^^^^^^^^^^^^
File “apps/erpnext/erpnext/accounts/report/financial_statements.py”, line 540, in get_accounting_entries
entries = query.run(as_dict=True)
^^^^^^^^^^^^^^^^^^^^^^^
File “apps/frappe/frappe/query_builder/utils.py”, line 87, in execute_query
result = frappe.db.sql(query, params, *args, **kwargs) # nosemgrep
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “apps/frappe/frappe/database/database.py”, line 234, in sql
self._cursor.execute(query, values)
File “env/lib/python3.12/site-packages/pymysql/cursors.py”, line 153, in execute
result = self._query(query)
^^^^^^^^^^^^^^^^^^
File “env/lib/python3.12/site-packages/pymysql/cursors.py”, line 322, in _query
conn.query(q)
File “env/lib/python3.12/site-packages/pymysql/connections.py”, line 563, in query
self._affected_rows = self._read_query_result(unbuffered=unbuffered)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “env/lib/python3.12/site-packages/pymysql/connections.py”, line 825, in _read_query_result
result.read()
File “env/lib/python3.12/site-packages/pymysql/connections.py”, line 1206, in read
self._read_result_packet(first_packet)
File “env/lib/python3.12/site-packages/pymysql/connections.py”, line 1283, in _read_result_packet
self._read_rowdata_packet()
File “env/lib/python3.12/site-packages/pymysql/connections.py”, line 1330, in _read_rowdata_packet
packet = self.connection._read_packet()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “env/lib/python3.12/site-packages/pymysql/connections.py”, line 775, in _read_packet
packet.raise_for_error()
File “env/lib/python3.12/site-packages/pymysql/protocol.py”, line 219, in raise_for_error
err.raise_mysql_exception(self._data)
File “env/lib/python3.12/site-packages/pymysql/err.py”, line 150, in raise_mysql_exception
raise errorclass(errno, errval)
pymysql.err.OperationalError: (1034, “Index for table ‘tabGL Entry’ is corrupt; try to repair it”)

Request Data

{
“type”: “POST”,
“args”: {
“report_name”: “Profit and Loss Statement”,
“filters”: “{"company":"Alfurat","filter_based_on":"Date Range","period_start_date":"2024-07-01","period_end_date":"2024-07-31","from_fiscal_year":"2024","to_fiscal_year":"2024","periodicity":"Monthly","presentation_currency":"IQD","selected_view":"Report","accumulated_values":1,"include_default_book_entries":1}”,
“ignore_prepared_report”: 1
},
“headers”: {},
“error_handlers”: {},
“url”: “/api/method/frappe.desk.query_report.run”,
“request_id”: null
}

Response Data

{
“exception”: “pymysql.err.OperationalError: (1034, "Index for table ‘tabGL Entry’ is corrupt; try to repair it")”,
“exc_type”: “OperationalError”,
“_exc_source”: “erpnext (app)”
}

You can try running OPTIMIZE TABLE <table_name> to fix the indexes.

If that doesn’t work, you need to find corrupt indexes by check table and re-create that.

Yeah, this is classic InnoDB index corruption on tabGL Entry. Since it’s an accounting table with tons of writes, once the index gets damaged MySQL just stops reading rows — that’s why your export drops from 186k to ~13k. REPAIR TABLE won’t ever work on InnoDB, so don’t bother with that.

What people usually try first is ALTER TABLE \tabGL Entry` FORCE;to rebuild the table, or a rawmysqldump --quick --single-transaction` which sometimes pulls more rows than phpMyAdmin. But if both bail out at the same spot, the tablespace is toast and MySQL won’t go past the corrupted pages.

At that point you pretty much need an external recovery tool. Stellar Repair for MySQL is one that actually works for this kind of thing because it reads the InnoDB pages directly and can recover rows MySQL refuses to touch. After that, recreate the table and import the recovered data. Sadly, that’s usually the only reliable way to get all 186k rows back.

contact me we will resolve that issue