Skip to content

tortoise migrate fails to insert migration info row into tortoise_migrations table when using MariaDB #2132

@RuslanUC

Description

@RuslanUC

Describe the bug
When trying to migrate MariaDB database, tortoise migrates executes migration successfully and then fails to insert migration info into tortoise_migrations with following message:

tortoise.exceptions.OperationalError: (1292, "Incorrect datetime value: '2026-03-04T18:06:51.535474+00:00' for column `tortoise-migrate-test`.`tortoise_migrations`.`applied_at` at row 1")

I searched about mariadb's issue with iso8601 and found that some mysql versions and all (or at least it seems like it) mariadb versions dont support iso8601 (which is used in record_applied method).

To Reproduce
Repo with example

uv add cryptography git+https://github.com/tortoise/tortoise-orm.git@develop[accel,asyncmy]

Add following code to pyproject.toml:

[tool.tortoise]
tortoise_orm = "main.TORTOISE_ORM"

main.py:

from datetime import datetime
from os import environ

from tortoise import fields, Model


class SomeModel(Model):
    id: int = fields.BigIntField(pk=True)
    dt: datetime = fields.DatetimeField()


TORTOISE_ORM = {
    "connections": {
        "default": environ["DB_CONNECTION_STRING"],
    },
    "apps": {
        "models": {
            "models": ["main"],
            "default_connection": "default",
            "migrations": "migrations",
        },
    },
}

Make and run migrations:

uv run tortoise makemigrations
uv run tortoise migrate

Expected behavior
No errors.

Additional context
Migration fails on MariaDB 10.6 and 11.8. Mysql 8+ has no issues.
Issue could be solved by not using .isoformat() and just passing applied_at to execute_insert (not execute_script used in record_applied).
Following diff solves the issue on mariadb (and breaks all other databases since they use ? or $ instead of %):

diff --git a/tortoise/migrations/recorder.py b/tortoise/migrations/recorder.py
index 5508d8d..2a9db21 100644
--- a/tortoise/migrations/recorder.py
+++ b/tortoise/migrations/recorder.py
@@ -74,13 +74,13 @@ class MigrationRecorder:
         return [MigrationKey(app_label=row["app"], name=row["name"]) for row in rows]
 
     async def record_applied(self, app: str, name: str) -> None:
-        applied_at = datetime.now(timezone.utc).isoformat()
+        applied_at = datetime.now(timezone.utc)
         query = (
             f"INSERT INTO {self._quote(self.table_name)} "  # nosec B608
             f"({self._quote('app')}, {self._quote('name')}, {self._quote('applied_at')}) "
-            f"VALUES ('{self._escape(app)}', '{self._escape(name)}', '{applied_at}')"
+            f"VALUES (%s, %s, %s)"
         )
-        await self.connection.execute_script(query)
+        await self.connection.execute_insert(query, [app, name, applied_at])
 
     async def record_unapplied(self, app: str, name: str) -> None:
         query = (

I can look into solving this in the next couple of days.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions