juicedata/juicefs · error

unmarshal worker config: %s

Error message

unmarshal worker config: %s

What it means

After reading and size-checking the payload, ReadClusterWorkerConfig unmarshals it as JSON into clusterWorkerConfig. This error wraps json.Unmarshal failure — the stdin payload is not valid JSON for the expected schema.

Source

Thrown at pkg/sync/cluster.go:421

	args = append(args, "--manager", address)
	if !config.Verbose && !config.Quiet {
		args = append(args, "-q")
	}
	return shellescape.EscapeArgs(args), payload, nil
}

// ReadClusterWorkerConfig reads storage URLs and environment variables from worker stdin.
func ReadClusterWorkerConfig(r io.Reader) (string, string, map[string]string, error) {
	data, err := io.ReadAll(io.LimitReader(r, maxClusterWorkerConfigSize+1))
	if err != nil {
		return "", "", nil, fmt.Errorf("read worker config: %s", err)
	}
	if len(data) > maxClusterWorkerConfigSize {
		return "", "", nil, fmt.Errorf("worker config is too large")
	}
	var config clusterWorkerConfig
	if err := json.Unmarshal(data, &config); err != nil {
		return "", "", nil, fmt.Errorf("unmarshal worker config: %s", err)
	}
	if config.Source == "" || config.Destination == "" {
		return "", "", nil, fmt.Errorf("worker config is missing source or destination")
	}
	return config.Source, config.Destination, config.Env, nil
}

func launchWorker(address string, config *Config, wg *sync.WaitGroup) {
	workers := strings.Split(strings.Join(config.Workers, ","), ",")
	for _, host := range workers {
		wg.Add(1)
		go func(host string) {
			defer wg.Done()
			// copy
			path, err := findSelfPath()
			if err != nil {
				logger.Errorf("find self path: %s", err)
				return

View on GitHub (pinned to c9a67b23e8)

Solutions

  1. Confirm the manager and worker run the same JuiceFS version (juicefs version on all nodes)
  2. Run the worker command directly without wrapper scripts/pipelines so stdin carries only the manager's JSON payload
  3. Dump the stdin payload (hexdump the input in a test harness) to inspect corruption or truncation
  4. Check the wrapped error text (%s) for the JSON offset/syntax detail

Example fix

// before
ssh worker 'cat extra.txt; juicefs sync ... --manager addr' < payload
// after
ssh worker 'juicefs sync ... --manager addr' < payload
Defensive patterns

Strategy: try-catch

Try / catch

src, dst, env, err := sync.ReadClusterWorkerConfig(os.Stdin)
if err != nil {
	if strings.Contains(err.Error(), "unmarshal worker config") {
	logger.Fatalf("invalid worker config JSON: %v — check binary version parity and stdin wrappers", err)
}
logger.Fatalf("worker config error: %v", err)
}

Prevention

When it happens

Trigger: The bytes piped to the worker's stdin are malformed or not JSON: manager/worker binary version skew with different payload formats, wrapper scripts injecting extra output into stdin, truncated payload, or feeding the worker a non-config stream.

Common situations: Mixed JuiceFS versions in a cluster (old manager JSON shape vs new worker); shell pipelines or SSH wrappers that alter stdin; users manually running a worker command and piping the wrong data.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06). Data as JSON: /api/errors/f7b3e3e66ac0111c. Report an issue: GitHub.