Skip to content

[bug-hunter] Serialization accepts empty created_at/expires_at instead of raising deserialization error #320

@github-actions

Description

@github-actions

Impact

BasicSerializationAdapter.load_dict() silently accepts empty strings for created_at/expires_at when date_format="isoformat". This allows invalid timestamp data to deserialize as if the fields were missing, which can hide corrupted records and skip expected validation failures.

Reproduction Steps

  1. Create and use an isolated venv, then run this script:
```bash
python -m venv /tmp/gh-aw/agent/venv
/tmp/gh-aw/agent/venv/bin/pip install beartype typing_extensions
PYTHONPATH=src /tmp/gh-aw/agent/venv/bin/python - <<'PY'
from key_value.aio._utils.serialization import BasicSerializationAdapter

adapter = BasicSerializationAdapter(date_format='isoformat', value_format='dict')

for field,value in (('created_at','not-a-date'),('created_at',''),('expires_at','not-a-date'),('expires_at','')):
    payload={'value':{'x':1}, field:value}
    try:
        adapter.load_dict(payload)
        print(field, repr(value), 'ACCEPTED')
    except Exception as e:
        print(field, repr(value), 'RAISED', type(e).__name__, str(e))
PY
```

Expected vs Actual

Expected: Empty strings for timestamp fields should be treated as invalid ISO datetime strings and raise DeserializationError (same as other invalid strings).

Actual: "not-a-date" raises DeserializationError, but "" is accepted and silently ignored.

Observed output:

```text
created_at 'not-a-date' RAISED DeserializationError Invalid datetime string: not-a-date
created_at '' ACCEPTED
expires_at 'not-a-date' RAISED DeserializationError Invalid datetime string: not-a-date
expires_at '' ACCEPTED
```

Failing Test

```python
from key_value.aio._utils.serialization import BasicSerializationAdapter
from key_value.aio.errors import DeserializationError


def test_load_dict_rejects_empty_created_at_isoformat() -> None:
    adapter = BasicSerializationAdapter(date_format="isoformat", value_format="dict")
    payload = {"value": {"x": 1}, "created_at": ""}

    try:
        adapter.load_dict(payload)
    except DeserializationError:
        return

    raise AssertionError("Expected DeserializationError for empty created_at")
```

Evidence

  • src/key_value/aio/_utils/serialization.py:79-82 uses truthiness checks:
    • if created_at := key_must_be(...):
    • if expires_at := key_must_be(...):
  • Because "" is falsy, parsing is skipped and no error is raised.
  • git blame shows these lines were introduced in commit 775c4fdf ("Simplify serialization date parsing", 2026-02-19).

Generated by Gh Aw Bug Hunter

  • expires on Feb 27, 2026, 11:18 AM UTC

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions