crowdsecurity/crowdsec · error

unable to read logs from service %s: %w

Error message

unable to read logs from service %s: %w

What it means

tailServiceAttempt opens a swarm service log stream with d.Client.ServiceLogs(ctx, service.ID, logOptions). If that API call fails, it wraps as "unable to read logs from service %s". It means the docker API refused the ServiceLogs request for the configured swarm service.

Source

Thrown at pkg/acquisition/modules/docker/run.go:751

	}
}

func (d *Source) tailServiceAttempt(ctx context.Context, service *ContainerConfig, outChan chan pipeline.Event, bo backoff.BackOff) error {
	// For services, we need to get the service logs using the service logs API
	// Docker service logs aggregates logs from all running tasks of the service
	logOptions := client.ServiceLogsOptions{
		ShowStdout: service.logOptions.ShowStdout,
		ShowStderr: service.logOptions.ShowStderr,
		Since:      service.logOptions.Since,
		Until:      service.logOptions.Until,
		Timestamps: service.logOptions.Timestamps,
		Follow:     service.logOptions.Follow,
		Tail:       service.logOptions.Tail,
		Details:    service.logOptions.Details,
	}
	dockerReader, err := d.Client.ServiceLogs(ctx, service.ID, logOptions)
	if err != nil {
		return fmt.Errorf("unable to read logs from service %s: %w", service.Name, err)
	}

	// Log connection (both initial and reconnections)
	service.logger.Info("connected to service logs")

	bo.Reset()

	// Service logs don't use TTY, so we always use the dlog reader
	reader := dlog.NewReader(dockerReader)
	scanner := bufio.NewScanner(reader)

	readerChan := make(chan string)
	readerTomb := &tomb.Tomb{}
	readerTomb.Go(func() error {
		return ReadTailScanner(scanner, readerChan, readerTomb)
	})

	for {

View on GitHub (pinned to 909b515798)

Solutions

  1. Ensure the configured docker_host is a swarm manager node: `docker service ls` must work with the same credentials/socket.
  2. Verify the service name/ID is current (`docker service ps <name>`); service IDs change on redeploys.
  3. With docker-socket-proxy, whitelist /services and /services/{id}/logs.
  4. Rely on the retry backoff — the attempt loop will re-resolve the service and reconnect.

Example fix

// before
docker_host: unix:///var/run/docker.sock  # on a worker node
// after — point at a manager node
docker_host: tcp://manager:2377
Defensive patterns

Strategy: retry

Validate before calling

// Go: confirm manager access and service existence first
services, err := cli.ServiceList(ctx, service.ListOptions{})
if err != nil {
	return fmt.Errorf("not a swarm manager or daemon unreachable: %w", err)
}
for _, s := range services {
	if s.Spec.Name == targetService {
		return s.ID, nil
	}
}
return "", fmt.Errorf("service %q not found", targetService)

Try / catch

err := d.tailServiceAttempt(ctx, service, outChan, bo)
if err != nil && strings.Contains(err.Error(), "unable to read logs from service") {
	time.Sleep(bo.NextBackOff())
	// retry, re-resolving service ID
}

Prevention

When it happens

Trigger: ServiceLogs called for a service ID that no longer exists (service removed/updated between discovery and tail), the node is not a swarm manager (ServiceLogs requires manager access), or the daemon/proxy rejects the /services/{id}/logs endpoint.

Common situations: Pointing the docker datasource at a worker node's socket instead of a manager; service redeployed with a new ID; docker-socket-proxy blocking /services routes; swarm mode left after `docker swarm leave`.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06). Data as JSON: /api/errors/c011212fc4d80496. Report an issue: GitHub.