Billionmail/BillionMail · error

rspamdadm not found

Error message

rspamdadm not found

What it means

InitWorkerController runs `rspamadm pw` inside the Rspamd Docker container to hash a generated controller password. If the exec call returns a nil result (no command output object), the code treats it as rspamadm being unavailable and returns this error. It guards against silently writing an empty password hash into worker-controller.inc.

Source

Thrown at core/internal/service/rspamd/worker_controller.go:47

	passwordPlain := grand.S(16)

	// Generate the worker controller configuration
	dk, err := docker.NewDockerAPI()

	if err != nil {
		return
	}

	defer dk.Close()

	res, err := dk.ExecCommandByName(context.Background(), consts.SERVICES.Rspamd, []string{"rspamadm", "pw", "-p", passwordPlain}, "root")

	if err != nil {
		return
	}

	if res == nil {
		err = errors.New("rspamdadm not found")
		return
	}

	_, err = public.WriteFile(wci, `count = 1;
password = "`+strings.TrimSpace(strings.SplitN(strings.TrimSpace(res.Output), "\n", 2)[0])+`";
secure_ip = "127.0.0.1";
secure_ip = "::1";
static_dir = "${WWWDIR}";`)

	if err != nil {
		return
	}

	// Save the password
	err = public.OptionsMgrInstance.SetOption(context.Background(), "rspamd_worker_controller_password", passwordPlain)

	if err != nil {
		return

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Ensure the Rspamd container is running and healthy: docker ps | grep rspamd
  2. Verify rspamadm exists in the container: docker exec <rspamd> which rspamadm
  3. Re-run initialization after the container is up (the error path is transient in most deploys)
  4. Check docker API logs for the failed exec and why it returned no result
  5. As a last resort, recreate the Rspamd container from the official BillionMail image

Example fix

// before
res, err := dk.ExecCommandByName(ctx, consts.SERVICES.Rspamd, []string{"rspamadm", "pw", "-p", passwordPlain}, "root")
if err != nil { return }
// after
if err := waitForContainerHealthy(ctx, consts.SERVICES.Rspamd); err != nil { return err }
res, err := dk.ExecCommandByName(ctx, consts.SERVICES.Rspamd, []string{"rspamadm", "pw", "-p", passwordPlain}, "root")
if err != nil { return }
if res == nil { return errors.New("rspamadm not found") }
Defensive patterns

Strategy: validation

Validate before calling

out, err := exec.Command("docker", "exec", rspamdContainer, "which", "rspamadm").Output()
if err != nil || len(out) == 0 {
    return fmt.Errorf("rspamadm unavailable in container %s", rspamdContainer)
}

Try / catch

if err := rspamd.InitWorkerController(); err != nil {
    if strings.Contains(err.Error(), "rspamdadm not found") {
        // wait for the rspamd container then retry once
        time.Sleep(10 * time.Second)
        return rspamd.InitWorkerController()
    }
    return err
}

Prevention

When it happens

Trigger: dk.ExecCommandByName succeeds without error but returns res == nil — e.g. the Rspamd container reported no result for the exec, the container is in a bad state, or the docker API returned an empty exec response.

Common situations: Rspamd container not fully started or restarting; rspamadm binary missing from the container image (unusual/minimal image); docker API proxy returning empty responses; running initialization before the mail stack is deployed.

Related errors


AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05). Data as JSON: /api/errors/951b71e0ffccbe73. Report an issue: GitHub.