Skip to content

Runtime decoding error when mapping MariaDB YEAR/SMALLINT to u16 using query_as! #4358

Closed

Description

@x0710

I have found these related issues/pull requests

I encountered an unexpected runtime decoding error while using sqlx::query_as! with MariaDB.

The application compiled successfully, but at runtime failed with:

error occurred while decoding column N:
out of range integral type conversion attempted

The affected column was either a MariaDB YEAR or SMALLINT column mapped to a Rust u16.

At the time the issue occurred, adding the following derives to the destination struct made the runtime error disappear:

#[derive(Debug, Serialize, ToSchema, Clone, sqlx::FromRow)]

Although query_as! should not require FromRow, and neither Clone nor FromRow should affect primitive value decoding, the behavior changed after adding these derives.

Unfortunately, I have not yet been able to construct a minimal reproducible example, so I cannot determine whether these derives are the root cause or whether they only changed the generated code path.


Query

sqlx::query_as!(
    Class,
    "SELECT id, major_id, name, enrollment_year, created_at, updated_at
     FROM classes
     WHERE id = ?",
    id
)
.fetch_one(pool)
.await

Expected Behavior

The runtime decoding behavior of query_as! should not depend on unrelated derives such as Clone or sqlx::FromRow.


Notes

I understand that this report is currently incomplete because I cannot provide a reliable minimal reproduction.

I'm opening this issue mainly to ask whether this behavior is already known or whether there are existing issues involving MariaDB, query_as!, and decoding YEAR/SMALLINT into u16.

If needed, I'd be happy to continue investigating and provide a minimal reproducible example.

Description

I encountered an unexpected decoding issue when using sqlx::query_as! with MariaDB.

When selecting a YEAR or SMALLINT column into a Rust u16 field, the query fails with:

error occurred while decoding column N:
out of range integral type conversion attempted

However, after deriving both:

#[derive(sqlx::FromRow, Clone)]

for the destination struct, the exact same query succeeds without changing:

  • SQL
  • database schema
  • database contents
  • field types

This behavior is unexpected because query_as! should not require FromRow, and Clone should not affect primitive value decoding.

I understand that this report is currently incomplete because I cannot provide a reliable minimal reproduction.

I'm opening this issue mainly to ask whether this behavior is already known or whether there are existing issues involving MariaDB, query_as!, and decoding YEAR/SMALLINT into u16.

If needed, I'd be happy to continue investigating and provide a minimal reproducible example.

Reproduction steps

The destination struct was similar to:

type Id = u64;
#[derive(Debug, Serialize, ToSchema)]
pub struct Class {
    pub id: Id,
    pub major_id: Id,
    pub name: String,
    pub enrollment_year: u16,
    pub created_at: OffsetDateTime,
    pub updated_at: OffsetDateTime,
}

After changing it to:

type Id = u64;
#[derive(Debug, Serialize, ToSchema, Clone, sqlx::FromRow)]
pub struct Class {
    pub id: Id,
    pub major_id: Id,
    pub name: String,
    pub enrollment_year: u16,
    pub created_at: OffsetDateTime,
    pub updated_at: OffsetDateTime,
}

the runtime error disappeared.

SQLx version

0.8.6

Enabled SQLx features

mysql, runtime-tokio, macros, time

Database server and version

10.6.23-MariaDB-0ubuntu0.22.04.1

Operating system

ArchLinux

Rust version

rustc 1.91.1 (ed61e7d7e 2025-11-07)

Activity

  1. x0710 commented on Aug 1, 2026

    @x0710
    Author

    I found that this error mysteriously disappeared after I split the project into multiple crates. I still don't know the exact cause of the issue, but I believe the underlying problem definitely still exists somewhere.

  2. reopened this on Aug 24, 2026
  3. x0710 commented on Aug 24, 2026

    @x0710
    Author

    Reason

    It looks like the MySQL YEAR type inference prefers u8 because it is the first compatible integer type in the recommendation list.

    Although YEAR is encoded as a one-byte value in the MySQL protocol, its SQL value domain is:

    1901 - 2155

    Therefore, u8 cannot represent all valid YEAR values.

    Expected behavior

    The inferred Rust type should be able to represent the complete range of valid MySQL YEAR values.

    Possible mappings:

    YEAR -> u16

    or:

    YEAR -> i32

    Alternatively, u8 should not be selected as the default recommended type for YEAR.

    Fix

    Using an explicit override works:

    SELECT enrollment_year as "enrollment_year: u16"
    FROM student
    

    but this requires users to know the internal inference behavior and manually override every YEAR column.

  4. flawand commented on Oct 16, 2026

    @flawand

    @x0710 I opened a PR to fix this issue can you have a look?

  5. x0710 commented on Oct 19, 2026

    @x0710
    Author

    I think the problem has been solved. Thank you.

  6. flawand commented on Oct 19, 2026

    @flawand

    @x0710 my PR is still open and I recreated the bug before I applied the fix. Are you sure this issue has been fixed?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions