AttributeError: 'NoneType' object has no attribute 'replace'

Recently i think since the last Update to version 25.04.2.3 i have trouble with an replication job.

I need to replicate Snapshots from Monday at 2PM therefore i set “By Snapshot creation time” → “Custom (0 14 * * mon) At 02:PM, only on Monday” in combination with " Matching regular expression " → .* and it worked for months, now i suddenly get this error.

“[2025/09/01 18:36:35] ERROR [replication_task__task_18] [zettarepl.replication.run] For task ‘task_18’ unhandled replication error AttributeError(”‘NoneType’ object has no attribute ‘replace’“)
Traceback (most recent call last):
File “/usr/lib/python3/dist-packages/zettarepl/replication/run.py”, line 181, in run_replication_tasks
retry_contains_partially_complete_state(
File “/usr/lib/python3/dist-packages/zettarepl/replication/partially_complete_state.py”, line 16, in retry_contains_partially_complete_state
return func()
^^^^^^
File “/usr/lib/python3/dist-packages/zettarepl/replication/run.py”, line 182, in
… 17 more lines …
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/usr/lib/python3/dist-packages/zettarepl/replication/task/should_replicate.py”, line 37, in replication_task_should_replicate_parsed_snapshot
replication_task.restrict_schedule.should_run(parsed_snapshot.datetime)
File “/usr/lib/python3/dist-packages/zettarepl/scheduler/cron.py”, line 38, in should_run
idealized = idealized_datetime(d)
^^^^^^^^^^^^^^^^^^^^^
File “/usr/lib/python3/dist-packages/zettarepl/utils/datetime.py”, line 11, in idealized_datetime
return d.replace(second=0, microsecond=0, tzinfo=None)
^^^^^^^^^
AttributeError: ‘NoneType’ object has no attribute ‘replace’”

How can i fix this ?

Just ran into the same problem on a replication task (local dataset to local dataset):

[2025/09/03 11:52:08] WARNING  [replication_task__task_1] [zettarepl.replication.run] For task 'task_1' at attempt 1 recoverable replication error RecoverableReplicationError("cannot open 'halde/truenas_homes@backup-hourly-2025-09-02_11-00': dataset does not exist\ncannot receive: failed to read from stream")
[2025/09/03 11:52:08] INFO     [replication_task__task_1] [zettarepl.replication.run] After recoverable error sleeping for 1 seconds
[2025/09/03 11:52:10] ERROR    [replication_task__task_1] [zettarepl.replication.run] For task 'task_1' unhandled replication error AttributeError("'NoneType' object has no attribute 'replace'")
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/zettarepl/replication/run.py", line 181, in run_replication_tasks
    retry_contains_partially_complete_state(
  File "/usr/lib/python3/dist-packages/zettarepl/replication/partially_complete_state.py", line 16, in retry_contains_partially_complete_state
    return func()
           ^^^^^^
  File "/usr/lib/python3/dist-packages/zettarepl/replication/run.py", line 182, in <lambda>
    lambda: run_replication_task_part(replication_task, source_dataset, src_context, dst_context,
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/zettarepl/replication/run.py", line 263, in run_replication_task_part
    pre_retention(src_context.context.now.replace(tzinfo=None), replication_task, src_context.datasets,
  File "/usr/lib/python3/dist-packages/zettarepl/replication/pre_retention.py", line 61, in pre_retention
    snapshots_to_destroy = calculate_snapshots_to_remove(owners, remote_snapshots)
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/zettarepl/retention/calculate.py", line 22, in calculate_snapshots_to_remove
    for snapshot in calculate_dataset_snapshots_to_remove(dataset_owners, dataset, dataset_snapshots)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/zettarepl/retention/calculate.py", line 58, in calculate_dataset_snapshots_to_remove
    snapshot_owners = [
                      ^
  File "/usr/lib/python3/dist-packages/zettarepl/retention/calculate.py", line 64, in <listcomp>
    owner.owns_snapshot(dataset, parsed_snapshot_name)
  File "/usr/lib/python3/dist-packages/zettarepl/replication/task/snapshot_owner.py", line 54, in owns_snapshot
    return replication_task_should_replicate_parsed_snapshot(self.replication_task, parsed_snapshot_name)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/zettarepl/replication/task/should_replicate.py", line 41, in replication_task_should_replicate_parsed_snapshot
    replication_task.schedule.should_run(parsed_snapshot.datetime)
  File "/usr/lib/python3/dist-packages/zettarepl/scheduler/cron.py", line 38, in should_run
    idealized = idealized_datetime(d)
                ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/zettarepl/utils/datetime.py", line 11, in idealized_datetime
    return d.replace(second=0, microsecond=0, tzinfo=None)
           ^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'replace'

Sorry sent it too early.

I am on 25.04.2.3 community. This is the initial sync of a new backup drive.

It appears that it doens’t like some of the existing snapshots? I have a schedule of hourly, daily, weekly and monthly snapshots.

Re-running the replication task makes it fail immediately again.

How can I get more information, for example which snapshot is the culprit? Can I enable debug output somehow?

I think it’s the same issue I reported here Replication fails after upgrading to 25.04.2.1 [EFAULT] '<' not supported between instances of 'NoneType' and 'datetime.datetime' but nobody answered

Interesting, thanks for cross-posting. I am too using a regexp to match the snapshots to replicate

Replicating to an empty dataset works, but the next try failes.

Here the log.

[2025/09/05 12:11:46] DEBUG [IoThread_17] [zettarepl.transport.local] [shell:21] [async_exec:21] Running [‘zfs’, ‘list’, ‘-t’, ‘snapshot’, ‘-H’, ‘-o’, ‘name’, ‘-s’, ‘name’, ‘-d’, ‘1’, ‘SSD’]
[2025/09/05 12:11:46] DEBUG [IoThread_17] [zettarepl.transport.local] [shell:21] [async_exec:21] Success: ‘SSD@auto-2025-09-05_11-15\n’
[2025/09/05 12:11:46] DEBUG [IoThread_0] [zettarepl.transport.local] [shell:22] [async_exec:22] Running [‘zfs’, ‘list’, ‘-t’, ‘snapshot’, ‘-H’, ‘-o’, ‘name’, ‘-s’, ‘name’, ‘-d’, ‘1’, ‘SSD’]
[2025/09/05 12:11:46] DEBUG [IoThread_0] [zettarepl.transport.local] [shell:22] [async_exec:22] Success: ‘SSD@auto-2025-09-05_11-15\n’
[2025/09/05 12:12:05] INFO [MainThread] [zettarepl.scheduler.clock] Interrupted
[2025/09/05 12:12:05] INFO [MainThread] [zettarepl.zettarepl] Scheduled tasks: [<Replication Task ‘task_2’>]
[2025/09/05 12:12:05] ERROR [retention] [zettarepl.zettarepl] Unhandled exception while running retention @cee:{“TNLOG”: {“exception”: “Traceback (most recent call last):\n File "/usr/lib/python3/dist-packages/zettarepl/zettarepl.py", line 328, in _run_retention\n self._run_local_retention(retention_datetime, pending_running_tasks)\n File "/usr/lib/python3/dist-packages/zettarepl/zettarepl.py", line 434, in _run_local_retention\n snapshots_to_destroy = calculate_snapshots_to_remove(owners, local_snapshots)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/zettarepl/retention/calculate.py", line 22, in calculate_snapshots_to_remove\n for snapshot in calculate_dataset_snapshots_to_remove(dataset_owners, dataset, dataset_snapshots)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/zettarepl/retention/calculate.py", line 58, in calculate_dataset_snapshots_to_remove\n snapshot_owners = [\n ^\n File "/usr/lib/python3/dist-packages/zettarepl/retention/calculate.py", line 64, in \n owner.owns_snapshot(dataset, parsed_snapshot_name)\n File "/usr/lib/python3/dist-packages/zettarepl/replication/task/snapshot_owner.py", line 54, in owns_snapshot\n return replication_task_should_replicate_parsed_snapshot(self.replication_task, parsed_snapshot_name)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/zettarepl/replication/task/should_replicate.py", line 37, in replication_task_should_replicate_parsed_snapshot\n replication_task.restrict_schedule.should_run(parsed_snapshot.datetime)\n File "/usr/lib/python3/dist-packages/zettarepl/scheduler/cron.py", line 38, in should_run\n idealized = idealized_datetime(d)\n ^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/zettarepl/utils/datetime.py", line 11, in idealized_datetime\n return d.replace(second=0, microsecond=0, tzinfo=None)\n ^^^^^^^^^\nAttributeError: ‘NoneType’ object has no attribute ‘replace’”, “type”: “PYTHON_EXCEPTION”, “time”: “2025-09-05 10:12:05.506806”}}
[2025/09/05 12:12:06] INFO [replication_task__task_2] [zettarepl.replication.run] Encrypted destination dataset ‘Storage/Rep-Test3’ does not have matching snapshots or data, destroying it
[2025/09/05 12:12:06] INFO [replication_task__task_2] [zettarepl.replication.pre_retention] Pre-retention destroying snapshots:
[2025/09/05 12:12:06] INFO [replication_task__task_2] [zettarepl.replication.run] For replication task ‘task_2’: doing push from ‘SSD’ to ‘Storage/Rep-Test3’ of snapshot=‘auto-2025-09-05_11-15’ incremental_base=None include_intermediate=False receive_resume_token=None encryption=False
[2025/09/05 12:12:07] INFO [replication_task__task_2] [zettarepl.replication.run] For replication task ‘task_2’: doing push from ‘SSD/.ix-virt’ to ‘Storage/Rep-Test3/.ix-virt’ of snapshot=‘auto-2025-09-05_11-15’ incremental_base=None include_intermediate=False receive_resume_token=None encryption=False
[2025/09/05 12:12:08] INFO [replication_task__task_2] [zettarepl.replication.run] For replication task ‘task_2’: doing push from ‘SSD/.ix-virt/buckets’ to ‘Storage/Rep-Test3/.ix-virt/buckets’ of snapshot=‘auto-2025-09-05_11-15’ incremental_base=None include_intermediate=False receive_resume_token=None encryption=False
[2025/09/05 12:12:08] INFO [replication_task__task_2] [zettarepl.replication.run] For replication task ‘task_2’: doing push from ‘SSD/.ix-virt/containers’ to ‘Storage/Rep-Test3/.ix-virt/containers’ of snapshot=‘auto-2025-09-05_11-15’ incremental_base=None include_intermediate=False receive_resume_token=None encryption=False
[2025/09/05 12:12:09] INFO [replication_task__task_2] [zettarepl.replication.run] For replication task ‘task_2’: doing push from ‘SSD/.ix-virt/containers/pbsbw’ to ‘Storage/Rep-Test3/.ix-virt/containers/pbsbw’ of snapshot=‘auto-2025-09-05_11-15’ incremental_base=None include_intermediate=False receive_resume_token=None encryption=False
[2025/09/05 12:12:13] INFO [replication_task__task_2] [zettarepl.replication.run] For replication task ‘task_2’: doing push from ‘SSD/.ix-virt/custom’ to ‘Storage/Rep-Test3/.ix-virt/custom’ of snapshot=‘auto-2025-09-05_11-15’ incremental_base=None include_intermediate=False receive_resume_token=None encryption=False
[2025/09/05 12:12:14] INFO [replication_task__task_2] [zettarepl.replication.run] For replication task ‘task_2’: doing push from ‘SSD/.ix-virt/custom/default_proxmox-backup-server_3.3-1_1742829103494.iso.iso’ to ‘Storage/Rep-Test3/.ix-virt/custom/default_proxmox-backup-server_3.3-1_1742829103494.iso.iso’ of snapshot=‘auto-2025-09-05_11-15’ incremental_base=None include_intermediate=False receive_resume_token=None encryption=False
[2025/09/05 12:12:18] INFO [replication_task__task_2] [zettarepl.replication.run] For replication task ‘task_2’: doing push from ‘SSD/.ix-virt/custom/default_testvolume’ to ‘Storage/Rep-Test3/.ix-virt/custom/default_testvolume’ of snapshot=‘auto-2025-09-05_11-15’ incremental_base=None include_intermediate=False receive_resume_token=None encryption=False
[2025/09/05 12:12:25] INFO [replication_task__task_2] [zettarepl.replication.run] For replication task ‘task_2’: doing push from ‘SSD/.ix-virt/deleted’ to ‘Storage/Rep-Test3/.ix-virt/deleted’ of snapshot=‘auto-2025-09-05_11-15’ incremental_base=None include_intermediate=False receive_resume_token=None encryption=False
[2025/09/05 12:12:25] INFO [replication_task__task_2] [zettarepl.replication.run] For replication task ‘task_2’: doing push from ‘SSD/.ix-virt/deleted/buckets’ to ‘Storage/Rep-Test3/.ix-virt/deleted/buckets’ of snapshot=‘auto-2025-09-05_11-15’ incremental_base=None include_intermediate=False receive_resume_token=None encryption=False
[2025/09/05 12:12:26] INFO [replication_task__task_2] [zettarepl.replication.run] For replication task ‘task_2’: doing push from ‘SSD/.ix-virt/deleted/containers’ to ‘Storage/Rep-Test3/.ix-virt/deleted/containers’ of snapshot=‘auto-2025-09-05_11-15’ incremental_base=None include_intermediate=False receive_resume_token=None encryption=False
[2025/09/05 12:12:27] INFO [replication_task__task_2] [zettarepl.replication.run] For replication task ‘task_2’: doing push from ‘SSD/.ix-virt/deleted/custom’ to ‘Storage/Rep-Test3/.ix-virt/deleted/custom’ of snapshot=‘auto-2025-09-05_11-15’ incremental_base=None include_intermediate=False receive_resume_token=None encryption=False
[2025/09/05 12:12:27] INFO [replication_task__task_2] [zettarepl.replication.run] For replication task ‘task_2’: doing push from ‘SSD/.ix-virt/deleted/images’ to ‘Storage/Rep-Test3/.ix-virt/deleted/images’ of snapshot=‘auto-2025-09-05_11-15’ incremental_base=None include_intermediate=False receive_resume_token=None encryption=False
[2025/09/05 12:12:28] INFO [replication_task__task_2] [zettarepl.replication.run] For replication task ‘task_2’: doing push from ‘SSD/.ix-virt/deleted/images/2a3f3a1c3c3b56cac8b8c4bd0fb4d0f429050240d3c618220346bf7a71153a2d’ to ‘Storage/Rep-Test3/.ix-virt/deleted/images/2a3f3a1c3c3b56cac8b8c4bd0fb4d0f429050240d3c618220346bf7a71153a2d’ of snapshot=‘readonly’ incremental_base=None include_intermediate=True receive_resume_token=None encryption=False
[2025/09/05 12:12:31] INFO [replication_task__task_2] [zettarepl.replication.run] For replication task ‘task_2’: doing push from ‘SSD/.ix-virt/deleted/images/2a3f3a1c3c3b56cac8b8c4bd0fb4d0f429050240d3c618220346bf7a71153a2d’ to ‘Storage/Rep-Test3/.ix-virt/deleted/images/2a3f3a1c3c3b56cac8b8c4bd0fb4d0f429050240d3c618220346bf7a71153a2d’ of snapshot=‘auto-2025-09-05_11-15’ incremental_base=‘readonly’ include_intermediate=True receive_resume_token=None encryption=False
[2025/09/05 12:12:31] INFO [replication_task__task_2] [zettarepl.replication.run] For replication task ‘task_2’: doing push from ‘SSD/.ix-virt/deleted/virtual-machines’ to ‘Storage/Rep-Test3/.ix-virt/deleted/virtual-machines’ of snapshot=‘auto-2025-09-05_11-15’ incremental_base=None include_intermediate=False receive_resume_token=None encryption=False
[2025/09/05 12:12:32] INFO [replication_task__task_2] [zettarepl.replication.run] For replication task ‘task_2’: doing push from ‘SSD/.ix-virt/images’ to ‘Storage/Rep-Test3/.ix-virt/images’ of snapshot=‘auto-2025-09-05_11-15’ incremental_base=None include_intermediate=False receive_resume_token=None encryption=False
[2025/09/05 12:12:33] INFO [replication_task__task_2] [zettarepl.replication.run] For replication task ‘task_2’: doing push from ‘SSD/.ix-virt/images/7c657dcda58a73c9083c2d38baedc6532385c457abdf8f257dc8e4c2703d0919’ to ‘Storage/Rep-Test3/.ix-virt/images/7c657dcda58a73c9083c2d38baedc6532385c457abdf8f257dc8e4c2703d0919’ of snapshot=‘readonly’ incremental_base=None include_intermediate=True receive_resume_token=None encryption=False
[2025/09/05 12:12:36] INFO [replication_task__task_2] [zettarepl.replication.run] For replication task ‘task_2’: doing push from ‘SSD/.ix-virt/images/7c657dcda58a73c9083c2d38baedc6532385c457abdf8f257dc8e4c2703d0919’ to ‘Storage/Rep-Test3/.ix-virt/images/7c657dcda58a73c9083c2d38baedc6532385c457abdf8f257dc8e4c2703d0919’ of snapshot=‘auto-2025-09-05_11-15’ incremental_base=‘readonly’ include_intermediate=True receive_resume_token=None encryption=False
[2025/09/05 12:12:36] INFO [replication_task__task_2] [zettarepl.replication.run] For replication task ‘task_2’: doing push from ‘SSD/.ix-virt/virtual-machines’ to ‘Storage/Rep-Test3/.ix-virt/virtual-machines’ of snapshot=‘auto-2025-09-05_11-15’ incremental_base=None include_intermediate=False receive_resume_token=None encryption=False
[2025/09/05 12:12:37] INFO [replication_task__task_2] [zettarepl.replication.run] For replication task ‘task_2’: doing push from ‘SSD/.ix-virt/virtual-machines/sadfagdfgsfd’ to ‘Storage/Rep-Test3/.ix-virt/virtual-machines/sadfagdfgsfd’ of snapshot=‘auto-2025-09-05_11-15’ incremental_base=None include_intermediate=False receive_resume_token=None encryption=False
[2025/09/05 12:12:38] INFO [replication_task__task_2] [zettarepl.replication.run] For replication task ‘task_2’: doing push from ‘SSD/.ix-virt/virtual-machines/sadfagdfgsfd.block’ to ‘Storage/Rep-Test3/.ix-virt/virtual-machines/sadfagdfgsfd.block’ of snapshot=‘auto-2025-09-05_11-15’ incremental_base=None include_intermediate=False receive_resume_token=None encryption=False
[2025/09/05 12:12:39] INFO [replication_task__task_2] [zettarepl.replication.run] For replication task ‘task_2’: doing push from ‘SSD/ISO’ to ‘Storage/Rep-Test3/ISO’ of snapshot=‘auto-2025-09-04_01-39’ incremental_base=None include_intermediate=True receive_resume_token=None encryption=False
[2025/09/05 12:12:43] INFO [replication_task__task_2] [zettarepl.replication.run] For replication task ‘task_2’: doing push from ‘SSD/ISO’ to ‘Storage/Rep-Test3/ISO’ of snapshot=‘auto-2025-09-05_11-15’ incremental_base=‘auto-2025-09-04_01-39’ include_intermediate=True receive_resume_token=None encryption=False
[2025/09/05 12:12:44] INFO [replication_task__task_2] [zettarepl.replication.run] For replication task ‘task_2’: doing push from ‘SSD/VMS’ to ‘Storage/Rep-Test3/VMS’ of snapshot=‘auto-2025-09-05_11-15’ incremental_base=None include_intermediate=False receive_resume_token=None encryption=False
[2025/09/05 12:12:44] INFO [replication_task__task_2] [zettarepl.replication.run] For replication task ‘task_2’: doing push from ‘SSD/VM_DATA’ to ‘Storage/Rep-Test3/VM_DATA’ of snapshot=‘auto-2025-09-05_11-15’ incremental_base=None include_intermediate=False receive_resume_token=None encryption=False
[2025/09/05 12:12:45] INFO [replication_task__task_2] [zettarepl.replication.run] For replication task ‘task_2’: doing push from ‘SSD/VM_DATA/test001’ to ‘Storage/Rep-Test3/VM_DATA/test001’ of snapshot=‘auto-2025-09-05_11-15’ incremental_base=None include_intermediate=False receive_resume_token=None encryption=False
[2025/09/05 12:12:51] INFO [retention] [zettarepl.zettarepl] Retention destroying local snapshots:
[2025/09/05 12:12:51] ERROR [retention] [zettarepl.zettarepl] Unhandled exception while running retention @cee:{“TNLOG”: {“exception”: “Traceback (most recent call last):\n File "/usr/lib/python3/dist-packages/zettarepl/zettarepl.py", line 329, in _run_retention\n self._run_remote_retention(retention_datetime, pending_running_tasks)\n File "/usr/lib/python3/dist-packages/zettarepl/zettarepl.py", line 468, in _run_remote_retention\n snapshots_to_destroy = calculate_snapshots_to_remove(owners, remote_snapshots)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/zettarepl/retention/calculate.py", line 22, in calculate_snapshots_to_remove\n for snapshot in calculate_dataset_snapshots_to_remove(dataset_owners, dataset, dataset_snapshots)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/zettarepl/retention/calculate.py", line 58, in calculate_dataset_snapshots_to_remove\n snapshot_owners = [\n ^\n File "/usr/lib/python3/dist-packages/zettarepl/retention/calculate.py", line 64, in \n owner.owns_snapshot(dataset, parsed_snapshot_name)\n File "/usr/lib/python3/dist-packages/zettarepl/replication/task/snapshot_owner.py", line 54, in owns_snapshot\n return replication_task_should_replicate_parsed_snapshot(self.replication_task, parsed_snapshot_name)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/zettarepl/replication/task/should_replicate.py", line 37, in replication_task_should_replicate_parsed_snapshot\n replication_task.restrict_schedule.should_run(parsed_snapshot.datetime)\n File "/usr/lib/python3/dist-packages/zettarepl/scheduler/cron.py", line 38, in should_run\n idealized = idealized_datetime(d)\n ^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/zettarepl/utils/datetime.py", line 11, in idealized_datetime\n return d.replace(second=0, microsecond=0, tzinfo=None)\n ^^^^^^^^^\nAttributeError: ‘NoneType’ object has no attribute ‘replace’”, “type”: “PYTHON_EXCEPTION”, “time”: “2025-09-05 10:12:51.425062”}}

So far so good, now the error.

[2025/09/05 12:15:08] INFO [MainThread] [zettarepl.scheduler.clock] Interrupted
[2025/09/05 12:15:08] INFO [MainThread] [zettarepl.zettarepl] Scheduled tasks: [<Replication Task ‘task_2’>]
[2025/09/05 12:15:08] ERROR [retention] [zettarepl.zettarepl] Unhandled exception while running retention @cee:{“TNLOG”: {“exception”: “Traceback (most recent call last):\n File "/usr/lib/python3/dist-packages/zettarepl/zettarepl.py", line 328, in _run_retention\n self._run_local_retention(retention_datetime, pending_running_tasks)\n File "/usr/lib/python3/dist-packages/zettarepl/zettarepl.py", line 434, in _run_local_retention\n snapshots_to_destroy = calculate_snapshots_to_remove(owners, local_snapshots)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/zettarepl/retention/calculate.py", line 22, in calculate_snapshots_to_remove\n for snapshot in calculate_dataset_snapshots_to_remove(dataset_owners, dataset, dataset_snapshots)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/zettarepl/retention/calculate.py", line 58, in calculate_dataset_snapshots_to_remove\n snapshot_owners = [\n ^\n File "/usr/lib/python3/dist-packages/zettarepl/retention/calculate.py", line 64, in \n owner.owns_snapshot(dataset, parsed_snapshot_name)\n File "/usr/lib/python3/dist-packages/zettarepl/replication/task/snapshot_owner.py", line 54, in owns_snapshot\n return replication_task_should_replicate_parsed_snapshot(self.replication_task, parsed_snapshot_name)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/zettarepl/replication/task/should_replicate.py", line 37, in replication_task_should_replicate_parsed_snapshot\n replication_task.restrict_schedule.should_run(parsed_snapshot.datetime)\n File "/usr/lib/python3/dist-packages/zettarepl/scheduler/cron.py", line 38, in should_run\n idealized = idealized_datetime(d)\n ^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/zettarepl/utils/datetime.py", line 11, in idealized_datetime\n return d.replace(second=0, microsecond=0, tzinfo=None)\n ^^^^^^^^^\nAttributeError: ‘NoneType’ object has no attribute ‘replace’”, “type”: “PYTHON_EXCEPTION”, “time”: “2025-09-05 10:15:08.544911”}}
[2025/09/05 12:15:10] ERROR [replication_task__task_2] [zettarepl.replication.run] For task ‘task_2’ unhandled replication error AttributeError(“‘NoneType’ object has no attribute ‘replace’”) @cee:{“TNLOG”: {“exception”: “Traceback (most recent call last):\n File "/usr/lib/python3/dist-packages/zettarepl/replication/run.py", line 181, in run_replication_tasks\n retry_contains_partially_complete_state(\n File "/usr/lib/python3/dist-packages/zettarepl/replication/partially_complete_state.py", line 16, in retry_contains_partially_complete_state\n return func()\n ^^^^^^\n File "/usr/lib/python3/dist-packages/zettarepl/replication/run.py", line 182, in \n lambda: run_replication_task_part(replication_task, source_dataset, src_context, dst_context,\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/zettarepl/replication/run.py", line 263, in run_replication_task_part\n pre_retention(src_context.context.now.replace(tzinfo=None), replication_task, src_context.datasets,\n File "/usr/lib/python3/dist-packages/zettarepl/replication/pre_retention.py", line 61, in pre_retention\n snapshots_to_destroy = calculate_snapshots_to_remove(owners, remote_snapshots)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/zettarepl/retention/calculate.py", line 22, in calculate_snapshots_to_remove\n for snapshot in calculate_dataset_snapshots_to_remove(dataset_owners, dataset, dataset_snapshots)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/zettarepl/retention/calculate.py", line 58, in calculate_dataset_snapshots_to_remove\n snapshot_owners = [\n ^\n File "/usr/lib/python3/dist-packages/zettarepl/retention/calculate.py", line 64, in \n owner.owns_snapshot(dataset, parsed_snapshot_name)\n File "/usr/lib/python3/dist-packages/zettarepl/replication/task/snapshot_owner.py", line 54, in owns_snapshot\n return replication_task_should_replicate_parsed_snapshot(self.replication_task, parsed_snapshot_name)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/zettarepl/replication/task/should_replicate.py", line 37, in replication_task_should_replicate_parsed_snapshot\n replication_task.restrict_schedule.should_run(parsed_snapshot.datetime)\n File "/usr/lib/python3/dist-packages/zettarepl/scheduler/cron.py", line 38, in should_run\n idealized = idealized_datetime(d)\n ^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/zettarepl/utils/datetime.py", line 11, in idealized_datetime\n return d.replace(second=0, microsecond=0, tzinfo=None)\n ^^^^^^^^^\nAttributeError: ‘NoneType’ object has no attribute ‘replace’”, “type”: “PYTHON_EXCEPTION”, “time”: “2025-09-05 10:15:10.254250”}}
[2025/09/05 12:15:10] INFO [retention] [zettarepl.zettarepl] Retention destroying local snapshots:
[2025/09/05 12:15:10] ERROR [retention] [zettarepl.zettarepl] Unhandled exception while running retention @cee:{“TNLOG”: {“exception”: “Traceback (most recent call last):\n File "/usr/lib/python3/dist-packages/zettarepl/zettarepl.py", line 329, in _run_retention\n self._run_remote_retention(retention_datetime, pending_running_tasks)\n File "/usr/lib/python3/dist-packages/zettarepl/zettarepl.py", line 468, in _run_remote_retention\n snapshots_to_destroy = calculate_snapshots_to_remove(owners, remote_snapshots)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/zettarepl/retention/calculate.py", line 22, in calculate_snapshots_to_remove\n for snapshot in calculate_dataset_snapshots_to_remove(dataset_owners, dataset, dataset_snapshots)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/zettarepl/retention/calculate.py", line 58, in calculate_dataset_snapshots_to_remove\n snapshot_owners = [\n ^\n File "/usr/lib/python3/dist-packages/zettarepl/retention/calculate.py", line 64, in \n owner.owns_snapshot(dataset, parsed_snapshot_name)\n File "/usr/lib/python3/dist-packages/zettarepl/replication/task/snapshot_owner.py", line 54, in owns_snapshot\n return replication_task_should_replicate_parsed_snapshot(self.replication_task, parsed_snapshot_name)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/zettarepl/replication/task/should_replicate.py", line 37, in replication_task_should_replicate_parsed_snapshot\n replication_task.restrict_schedule.should_run(parsed_snapshot.datetime)\n File "/usr/lib/python3/dist-packages/zettarepl/scheduler/cron.py", line 38, in should_run\n idealized = idealized_datetime(d)\n ^^^^^^^^^^^^^^^^^^^^^\n File "/usr/lib/python3/dist-packages/zettarepl/utils/datetime.py", line 11, in idealized_datetime\n return d.replace(second=0, microsecond=0, tzinfo=None)\n ^^^^^^^^^\nAttributeError: ‘NoneType’ object has no attribute ‘replace’”, “type”: “PYTHON_EXCEPTION”, “time”: “2025-09-05 10:15:10.543000”}}

I’ve been investigating a replication issue that appeared after a recent TrueNAS SCALE update and have made significant progress in identifying the root cause.

Problem Description:
Replication tasks fail with the error:

AttributeError: ‘NoneType’ object has no attribute ‘replace’

and subsequently

AttributeError: ‘NoneType’ object has no attribute ‘time’

The error occurs specifically when using “Replicate Specific Snapshots” or “Only Replicate Snapshots Matching Schedule” options.

Key Discovery:
I conducted a comparative analysis between systems and found that:

  • :white_check_mark: TrueNAS with Python 3.11.2 : Replication works perfectly
  • :x: TrueNAS with Python 3.11.9 : Replication fails with above errors

Critical Finding:
Using diff -s comparison, I confirmed that the relevant Python files (cron.py , datetime.py , and should_replicate.py ) are identical between both systems. The only difference is the Python version.

Files compared:

  • /usr/lib/python3/dist-packages/zettarepl/scheduler/cron.py
  • /usr/lib/python3/dist-packages/zettarepl/utils/datetime.py
  • /usr/lib/python3/dist-packages/zettarepl/replication/task/should_replicate.py

Conclusion:
This appears to be a Python 3.11.9 compatibility issue where the newer version handles None values or datetime parsing more strictly than 3.11.2. The zettarepl code doesn’t adequately handle cases where parsed_snapshot.datetime returns None .

This reproducible case (working in 3.11.2 vs. broken in 3.11.9) should help developers quickly identify and fix the compatibility issue in an upcoming update.

Any updates? I my replication task to rsync.net is failing because of this error