You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
SourceImageCollection.sample_interval() no longer accepts max_num, but capture sets created before that signature change still carry max_num in their stored kwargs JSON. Running "Populate capture set" on any such collection fails immediately with a TypeError, and because the JobType.run() path doesn't wrap the failure, the Job row stays stuck at STARTED / progress=0.1 forever (no error visible in the UI, no entry in progress.errors).
Observed on a production deployment (2026-04-23): a capture set with method="interval" and kwargs = {"max_num": 10, "date_end": "...", "date_start": "...", "minute_interval": 10, "deployment_ids": None, "event_ids": None} produced:
SourceImageCollection.sample_interval() got an unexpected keyword argument 'max_num'
Celery AsyncResult.state == FAILURE, but Job.status == STARTED in the DB.
Why it happens
populate_sample (ami/main/models.py around L4172) does method(**self.kwargs) — splats whatever JSON is stored into the sampling method, no filtering.
sample_interval (~L4315) was split off from the deprecated sample_common_combined in Updates to collection sampling methods #717 (Feb 2025) and doesn't accept max_num. Only the deprecated method supports it.
Stored kwargs from before the split still contain max_num, so every retry throws.
Proposed fix
Make max_num a uniformly supported parameter on all sample methods where it's meaningful:
sample_interval — accept max_num: int | None = None, apply as a per-deployment or global cap on the interval-sampled output (the old sample_common_combined path did this).
sample_random — already has size; consider accepting max_num as an alias so UI/stored-kwargs can be consistent across methods.
sample_random_from_each_event, sample_last_and_random_from_each_event, sample_greatest_file_size_from_each_event — these already take num_each; separate axis, leave alone unless unifying.
sample_manual, sample_positional, sample_nth, sample_detections_only, sample_full — max_num doesn't apply or isn't meaningful; fine to ignore.
Additionally (could be a separate issue, but related and worth noting here): populate_sample should either (a) filter self.kwargs against the target method's signature before splatting, or (b) the JobType.run() wrapper should catch unhandled exceptions and mark the Job FAILURE with the traceback in progress.errors — otherwise any signature drift between stored kwargs and a sampling method silently stalls jobs.
Repro
Create a capture set with method="interval" and any kwargs dict containing max_num (pre-split collections have this naturally; new ones can be created via Django shell).
Trigger "Populate capture set".
Job stays at STARTED / 0.1 indefinitely. Check the celery task id's state — it's FAILURE with the TypeError above.
Acceptance
sample_interval accepts max_num without raising.
A capture set with the example kwargs above populates successfully.
Job failures in populate_captures_collection surface in the UI (either via wrapper catching exceptions or populate_sample validating kwargs up-front).
Summary
SourceImageCollection.sample_interval()no longer acceptsmax_num, but capture sets created before that signature change still carrymax_numin their storedkwargsJSON. Running "Populate capture set" on any such collection fails immediately with aTypeError, and because theJobType.run()path doesn't wrap the failure, the Job row stays stuck atSTARTED / progress=0.1forever (no error visible in the UI, no entry inprogress.errors).Observed on a production deployment (2026-04-23): a capture set with
method="interval"andkwargs = {"max_num": 10, "date_end": "...", "date_start": "...", "minute_interval": 10, "deployment_ids": None, "event_ids": None}produced:Celery
AsyncResult.state == FAILURE, butJob.status == STARTEDin the DB.Why it happens
populate_sample(ami/main/models.pyaround L4172) doesmethod(**self.kwargs)— splats whatever JSON is stored into the sampling method, no filtering.sample_interval(~L4315) was split off from the deprecatedsample_common_combinedin Updates to collection sampling methods #717 (Feb 2025) and doesn't acceptmax_num. Only the deprecated method supports it.max_num, so every retry throws.Proposed fix
Make
max_numa uniformly supported parameter on all sample methods where it's meaningful:sample_interval— acceptmax_num: int | None = None, apply as a per-deployment or global cap on the interval-sampled output (the oldsample_common_combinedpath did this).sample_random— already hassize; consider acceptingmax_numas an alias so UI/stored-kwargs can be consistent across methods.sample_random_from_each_event,sample_last_and_random_from_each_event,sample_greatest_file_size_from_each_event— these already takenum_each; separate axis, leave alone unless unifying.sample_manual,sample_positional,sample_nth,sample_detections_only,sample_full—max_numdoesn't apply or isn't meaningful; fine to ignore.Additionally (could be a separate issue, but related and worth noting here):
populate_sampleshould either (a) filterself.kwargsagainst the target method's signature before splatting, or (b) theJobType.run()wrapper should catch unhandled exceptions and mark the JobFAILUREwith the traceback inprogress.errors— otherwise any signature drift between stored kwargs and a sampling method silently stalls jobs.Repro
method="interval"and anykwargsdict containingmax_num(pre-split collections have this naturally; new ones can be created via Django shell).STARTED / 0.1indefinitely. Check the celery task id's state — it'sFAILUREwith theTypeErrorabove.Acceptance
sample_intervalacceptsmax_numwithout raising.populate_captures_collectionsurface in the UI (either via wrapper catching exceptions orpopulate_samplevalidating kwargs up-front).