Skip to content

Capture set population fails with stored kwargs: sample_interval() rejects max_num #1266

Description

@mihow

Summary

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_fullmax_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

  1. 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).
  2. Trigger "Populate capture set".
  3. 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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    PSv2Async & distributed ML backend (PSv2): job state, NATS dispatch, result handling. Umbrella #515.

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions