Scan status reports COMPLETED after an enricher emits a FAILED event
Summary
When an enricher fails, the scan it belongs to still ends in COMPLETED. The failure is recorded as a FAILED event on the sketch event log, but nothing propagates it to the scan record, and GET /api/scans/{id} exposes only {id, sketch_id, status} — no error field. From the API (and therefore anything built on it), a failed enrichment is indistinguishable from an enricher that ran fine and legitimately found nothing.
Version
v1.2.12 (docker-compose.prod.yml, ghcr.io/reconurge/flowsint-api:1.2.12)
Reproduction
Easiest trigger is any Docker-backed enricher when the API/celery container can't reach the Docker daemon — which is the out-of-the-box state if the host's docker.sock is root:docker 0660, since the image runs as uid 1001 and isn't in that group.
Bring up the prod stack without granting the container the host's docker group.
Add a
domainnode and launchdomain_to_dns(ordomain_to_tls,domain_to_subdomains).GET /api/events/sketch/{sketch_id}/logs→{"type": "FAILED", "payload": {"message": "[DNSX] Failed to initialize dnsx: Failed to connect to Docker daemon. Is Docker running? Error: ... PermissionError(13, 'Permission denied')"}} {"type": "COMPLETED", "payload": {"message": "Enricher domain_to_dns finished."}}GET /api/scans/{scan_id}→{"id": "...", "sketch_id": "...", "status": "COMPLETED"}
The FAILED event and the COMPLETED event are emitted ~10 ms apart for the same enricher.
Expected
A scan whose enricher emitted FAILED should end in a failed terminal state, and the scan record should carry enough detail (error message, or at least a failed flag) for a caller to tell "the tool never ran" from "the tool ran and found nothing".
Impact
This is the sort of tool where an empty result gets read as a finding. Silently downgrading "the enricher could not run" to "COMPLETED, no new nodes" means an investigator can conclude a domain has no subdomains / no DNS records / no TLS when in fact nothing was ever queried. Anyone automating against the API has no signal at all, since the failure only exists in the event log.
Notes
Granting the container the host docker gid (group_add) fixes the underlying Docker permission error and those enrichers then work correctly — but the reporting behaviour is independent of that particular cause: any enricher that emits FAILED will still land on COMPLETED.
Also observed while testing: celery logs a pydantic.ValidationError from the emit_event task (1 validation error for Event ... dict_type) around the same failure path, in case the two are related.
Source: reconurge/flowsint