multica-ai/multica · error

parse response: %w

Error message

parse response: %w

What it means

After a 200 response from the daemon, runRepoCheckout json.Unmarshals the body into {path, branch_name}. This fails when the body is not valid JSON — i.e. the daemon returned 200 with a non-JSON payload (proxy interpolation, HTML error page, or a version-skewed daemon returning a different serialization).

Source

Thrown at server/cmd/multica/cmd_repo.go:418

			case <-ctx.Done():
				timer.Stop()
				return fmt.Errorf("connect to daemon: %w", context.Cause(ctx))
			case <-timer.C:
				continue
			}
		}
		if resp.StatusCode != http.StatusOK {
			return fmt.Errorf("checkout failed: %s", string(body))
		}
		break
	}

	var result struct {
		Path       string `json:"path"`
		BranchName string `json:"branch_name"`
	}
	if err := json.Unmarshal(body, &result); err != nil {
		return fmt.Errorf("parse response: %w", err)
	}

	fmt.Fprintf(os.Stdout, "%s\n", result.Path)
	fmt.Fprintf(os.Stderr, "Checked out %s → %s (branch: %s)\n", repoURL, result.Path, result.BranchName)

	return nil
}

func repoCheckoutRetryDelay(value string, now time.Time) time.Duration {
	const (
		defaultDelay = time.Second
		maxDelay     = 30 * time.Second
	)
	value = strings.TrimSpace(value)
	if seconds, err := strconv.Atoi(value); err == nil && seconds >= 0 {
		return min(time.Duration(seconds)*time.Second, maxDelay)
	}
	if retryAt, err := http.ParseTime(value); err == nil {

View on GitHub (pinned to 2c0912b6ec)

Solutions

  1. Confirm the port really belongs to the multica daemon: curl -s http://127.0.0.1:$MULTICA_DAEMON_PORT/repo/checkout and inspect what answers; fix the port collision.
  2. Upgrade/downgrade CLI and daemon to matching versions so the response contract ({path, branch_name}) holds.
  3. Re-run inside a fresh daemon task so MULTICA_DAEMON_PORT is injected by the real daemon.

Example fix

# before: another process owns the port
MULTICA_DAEMON_PORT=8080 multica repo checkout URL   # parse response: invalid character '<'

# after: use the daemon's actual port
ss -ltnp | grep multica   # e.g. 8123
MULTICA_DAEMON_PORT=8123 multica repo checkout URL
Defensive patterns

Strategy: try-catch

Validate before calling

if !json.Valid(body) {
	return fmt.Errorf("daemon returned non-JSON (wrong port or version?): %.120s", body)
}

Type guard

type checkoutResult struct {
	Path       string `json:"path"`
	BranchName string `json:"branch_name"`
}
func isCheckoutResult(v any) (checkoutResult, bool) {
	r, ok := v.(checkoutResult)
	return r, ok && r.Path != ""
}

Try / catch

if err := json.Unmarshal(body, &result); err != nil {
	return fmt.Errorf("parse response: %w (raw: %.120s)", err, body) // keep raw body for diagnosis
}

Prevention

When it happens

Trigger: A proxy/sidecar on 127.0.0.1 intercepts the port and answers 200 with HTML; an older/newer daemon binary whose /repo/checkout success payload is not the expected JSON; a truncated body after a transport hiccup (though ReadAll would usually catch that first).

Common situations: Another service already bound to the port the env var points at (port collision), so the CLI talks to the wrong process; mixed-version deployments during rolling upgrades of the daemon.

Related errors


AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15). Data as JSON: /api/errors/89d3b77e01985af8. Report an issue: GitHub.