Skip to content

assertCompatibleKubectlVersionExists throws Invalid version. Must be a string. Got type "object" when kubectl reports an unparseable gitVersion #2360

Description

@rparsonsbb

What happened

Every pulumi preview/up that constructs an eks.Cluster fails during Construct with:

error: <pkg>:index:<Component> resource 'my-cluster' has a problem: Invalid version. Must be a string. Got type "object".
error: Error: failed to register new resource my-cluster [...]: 2 UNKNOWN: resource monitor shut down

Stack trace (obtained by patching semver's thrown message, see below):

at new SemVer (node_modules/semver/classes/semver.js:21:97)
at compare (node_modules/semver/functions/compare.js:5:3)
at Object.lt (node_modules/semver/functions/lt.js:4:29)
at assertCompatibleKubectlVersionExists (node_modules/@pulumi/eks/dependencies.js:45)
at createCore (node_modules/@pulumi/eks/cluster.js:408)
at createCluster (node_modules/@pulumi/eks/cluster.js:1916)
at new Cluster (node_modules/@pulumi/eks/cluster.js:1802)

Root cause

nodejs/eks/dependencies.ts, assertCompatibleKubectlVersionExists:

const kcVersion = semver.clean(kctlVersion.clientVersion.gitVersion, { loose: true });
if (semver.lt(kcVersion, minKubectlVersion)) {

semver.clean() returns null for any input it cannot parse — it does not throw. That null is
passed unguarded into semver.lt(), which calls new SemVer(null) and throws
TypeError: Invalid version. Must be a string. Got type "object".

The message is actively misleading: the value is null, not an object. It only says "object"
because typeof null === "object". Anyone reading it reasonably concludes some object is being
passed where a version string belongs, and goes looking through Pulumi's provider/plugin version
plumbing — which is nowhere near the actual problem.

How to reproduce

Any kubectl whose gitVersion is not semver-cleanable. A build without version stamping is the
common case:

$ kubectl version --client=true --output=json
{
  "clientVersion": {
    "major": "",
    "minor": "",
    "gitVersion": "v0.0.0-master+$Format:%H$",
    ...
  }
}
> semver.clean("v0.0.0-master+$Format:%H$", { loose: true })
null
> semver.lt(null, "1.24.0")
TypeError: Invalid version. Must be a string. Got type "object".

We hit this on the public.ecr.aws/pulumi/pulumi:3.256.0 image used by Pulumi Deployments, whose
/usr/bin/kubectl is an unstamped build (filed separately against pulumi-docker-containers). But
the defect here is independent of that image: any unstamped or vendor-patched kubectl triggers it,
and the resulting error gives the user nothing to act on.

Note which.sync("kubectl") succeeds and JSON.parse succeeds, so neither of the two existing
guards fires — the function's own error paths are bypassed and it dies on a raw TypeError instead.

Suggested fix

Guard the null and report the value actually seen:

const gitVersion = kctlVersion.clientVersion?.gitVersion;
const kcVersion = semver.clean(gitVersion ?? "", { loose: true });
if (kcVersion === null) {
    throw new Error(
        `Could not parse the kubectl version from \`kubectl version --client=true --output=json\`. ` +
        `Received gitVersion: ${JSON.stringify(gitVersion)}. ` +
        `At least v${minKubectlVersion} of kubectl is required.`,
    );
}
if (semver.lt(kcVersion, minKubectlVersion)) { ... }

assertCompatibleAWSCLIExists immediately below has the same shape — semver.clean(version, {loose: true})
feeding semver.major()/semver.lt() with no null check — so it will fail the same way against an
aws --version string it cannot parse. Worth fixing both in one pass.

Environment

  • @pulumi/eks 2.8.1
  • @pulumi/pulumi 3.244.0, Pulumi CLI 3.256.0
  • Node.js 26.6.0, linux/amd64

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions