{"record":{"id":"35294a5a9ff66b52","repo":"multica-ai/multica","slug":"multica-cli-version-not-reported-by-daemon","errorCode":null,"errorMessage":"multica CLI version not reported by daemon","messagePattern":"multica CLI version not reported by daemon","errorType":"validation","errorClass":"ErrCLIVersionMissing","httpStatus":null,"severity":"error","filePath":"server/pkg/agent/version.go","lineNumber":88,"sourceCode":"\t}\n\tif devDescribeRe.MatchString(d) {\n\t\treturn true\n\t}\n\tparsed, err := parseSemver(d)\n\tif err != nil {\n\t\treturn false\n\t}\n\tmin, err := parseSemver(MinHandoffCLIVersion)\n\tif err != nil {\n\t\treturn false\n\t}\n\treturn !parsed.lessThan(min)\n}\n\n// Errors returned by CheckMinCLIVersion. Callers branch on these to surface\n// \"needs upgrade\" vs \"version not reported\" with the right user message.\nvar (\n\tErrCLIVersionMissing = errors.New(\"multica CLI version not reported by daemon\")\n\tErrCLIVersionTooOld  = errors.New(\"multica CLI version is below required minimum\")\n)\n\n// devDescribeRe matches the `git describe --tags --always --dirty` output for\n// a build past the latest tag, e.g. `v0.2.15-235-gdaf0e935` (optionally with a\n// trailing `-dirty`). Daemons built from source (Makefile `make build` / `make\n// daemon`) report this shape; tagged releases are bare semver. Treating dev-\n// described daemons as OK keeps `make daemon` unblocked without weakening the\n// gate for staging or production users running stale stable releases.\nvar devDescribeRe = regexp.MustCompile(`^v?\\d+\\.\\d+\\.\\d+-\\d+-g[0-9a-fA-F]+`)\n\n// CheckMinCLIVersion returns nil when `detected` parses as ≥ minimum. Returns\n// ErrCLIVersionMissing for empty or unparsable input, and ErrCLIVersionTooOld\n// when parsable but below the minimum. The caller can check for these\n// sentinel errors with errors.Is to drive the response shape.\n//\n// Dev-built daemons (git-describe shape) always pass — the version string\n// itself is the shared signal, so the modal pre-check and this server gate","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/multica-ai/multica/blob/2c0912b6ec764b373d44eeea1e80f0d9f11ab417/server/pkg/agent/version.go#L70-L106","documentation":"ErrCLIVersionMissing is returned by CheckMinCLIVersion when the daemon did not report any CLI/handoff version to compare against MinHandoffCLIVersion. It is distinct from ErrCLIVersionTooOld: this error means version detection failed entirely (empty/unparseable report), not that a valid version was below the minimum. Callers branch on the two sentinels to show 'version not reported' vs 'needs upgrade'. Note dev-build version strings matching `git describe` output are treated as OK so `make daemon` stays unblocked.","triggerScenarios":"Calling CheckMinCLIVersion with a detected version string that is empty or not reported by the daemon — e.g. an old daemon built before version reporting was added, or a daemon whose version probe failed and yielded an empty string.","commonSituations":"Running a stale daemon from before the handoff-version feature; a partially upgraded deployment where the CLI is new but the daemon was not restarted/upgraded; a custom daemon build that strips version info.","solutions":["Upgrade the daemon to a release that reports the multica CLI version, then retry.","Restart the daemon if it was upgraded in place but is still running the old binary.","Verify with the daemon's status/version endpoint that a non-empty version is now reported before re-invoking the handoff."],"exampleFix":"// before\nif err := version.CheckMinCLIVersion(detected); err != nil {\n    return fmt.Errorf(\"handoff blocked: %v\", err) // opaque\n}\n\n// after\nif err := version.CheckMinCLIVersion(detected); err != nil {\n    if errors.Is(err, version.ErrCLIVersionMissing) {\n        return fmt.Errorf(\"daemon did not report a CLI version; upgrade and restart the daemon\")\n    }\n    if errors.Is(err, version.ErrCLIVersionTooOld) {\n        return fmt.Errorf(\"daemon CLI too old for handoff; upgrade to >= %s\", version.MinHandoffCLIVersion)\n    }\n    return err\n}","handlingStrategy":"try-catch","validationCode":"// Before starting a handoff, confirm the daemon reports a version\ndetected := daemon.ReportedCLIVersion()\nif detected == \"\" {\n    return errors.New(\"daemon reports no CLI version; upgrade/restart the daemon before handoff\")\n}","typeGuard":null,"tryCatchPattern":"if err := version.CheckMinCLIVersion(detected); err != nil {\n    switch {\n    case errors.Is(err, version.ErrCLIVersionMissing):\n        // show 'version not reported' + upgrade-daemon guidance; not fixable client-side\n    case errors.Is(err, version.ErrCLIVersionTooOld):\n        // show minimum version and upgrade instructions\n    }\n}","preventionTips":["Upgrade the daemon before the CLI so version reporting exists when handoffs are attempted.","Monitor the daemon's reported version in health checks so missing version reports surface before a handoff is blocked.","Note that git-describe dev builds are accepted — dev daemons will not trip the gate."],"tags":["version","daemon","handoff","upgrade","cli"],"backgroundTag":null,"analyzedSha":"2c0912b6ec764b373d44eeea1e80f0d9f11ab417","analyzedAt":"2026-08-15T13:25:18.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}