dotCMS / dotCMS/core

SDK packaging: malformed published version strings and floating/orphaned dist-tags in SDK sources and example apps

Open
#36,891 2 comments 0 reactions 1 assignee View on GitHub

@KevinDavilaDotCMS is already working on this.

Since Sep 4, 2026.

  • #37414 by @KevinDavilaDotCMS — closed without merging
  • #37452 by @KevinDavilaDotCMS — merged
dotCMS : SDK OKR : Customer Support Team : Scout Type : Defect
Dominant language
Java
Stars
970
Forks
486
Avg merge
3d 33m
Merged PRs (30d)
170

Description

Problem Statement

Two related packaging defects in the @dotcms/* SDK. All claims below were reproduced locally against the live npm registry; the observed behaviour is recorded per package manager because it differs.

Defect A — malformed version strings in published packages (affects current latest)

@dotcms/client@26.8.3-1 and its siblings are published with a version string that is invalid semver and does not match the version the registry advertises:

Source Value
Registry metadata for @dotcms/client@26.8.3-1 26.8.3-1
The same package's tarball-internal package.json version 26.08.03-01
@dotcms/react@26.8.3-1dependencies["@dotcms/client"] 26.08.03-01

26.08.03-01 has leading zeros in its numeric identifiers, so it is not valid semver, and no such version exists on npm. npm normalises the registry version field on publish, but it does not normalise the tarball contents or dependency range strings, so the malformed value survives into what customers install.

Observed consequences:

  • Anything reading the installed package reports a nonexistent version. require('@dotcms/client/package.json').version returns 26.08.03-01. This affects SBOM/licence scanners, vulnerability tooling, bundler metadata and release tagging.
  • With yarn, the nested copy on disk literally reports 26.08.03-01, so the installed tree cannot be mapped back to a published release.
  • Resolution currently succeeds only because npm and pnpm apply loose-mode semver coercion. I verified npm 11 and pnpm 11 both resolve it today. Any consumer or resolver using strict semver parsing would fail. This is a latent break, not a current one.

This is present on the newest published packages, so it is a live defect rather than a historical one.

Defect B — floating and orphaned dist-tags in source and examples

B1. SDK library sources still declare "latest" in dependencies. On main:

File Field Value
core-web/libs/sdk/react/package.json dependencies "@dotcms/client": "latest", "@dotcms/uve": "latest"
core-web/libs/sdk/angular/package.json dependencies "@dotcms/client": "latest", "@dotcms/uve": "latest"
core-web/libs/sdk/experiments/package.json peerDependencies all four @dotcms/* at "latest"

core-web/bump-sdk-versions.js only rewrites peerDependencies for SDK libs (updatePeerDependencies) and dependencies for examples (updateDependenciesInExamples). It never rewrites the SDK libs' own dependencies. The current release pipeline masks this by injecting an exact pin at publish time, but the source is still wrong, and the entire published 1.x line, 1.0.6 (2025-09-04) through 1.7.0 (2026-07-13), shipped with "@dotcms/client": "latest".

The customer-facing effect is package-manager dependent. Reproduced with a manifest pinning @dotcms/client, @dotcms/react, @dotcms/uve, @dotcms/types all at 1.2.0:

Resolver Result with the pin alone Affected
npm 11 dedupes react's "latest" spec down to the root 1.2.0 No
yarn 1.22 installs a second copy at node_modules/@dotcms/react/node_modules/@dotcms/client, reporting 26.08.03-01 Yes
pnpm 11 keeps both copies; @dotcms/react's symlink points at @dotcms+client@26.8.3-1 Yes

So on yarn and pnpm a customer's explicit version pin is silently overridden, and @dotcms/react imports from @dotcms/client at runtime (it references buildPageQuery / createDotCMSClient), so react's code path uses the wrong client. npm users are not affected. Since npm immutability means the 1.x tarballs can never be corrected, this is permanent for the version range support recommends for older servers.

Note --legacy-peer-deps is not a mitigation: the "latest" is in dependencies, which that flag does not affect.

B2. Example apps pin floating dist-tags, and two pin an orphaned tag. On main:

Example Pinned tag Resolves to
examples/nextjs next 26.7.27-1-next.2448, frozen 2026-07-29
examples/vuejs next 26.7.27-1-next.2448, frozen 2026-07-29
examples/angular latest 26.8.3-1, floats forever
examples/astro latest 26.8.3-1, floats forever

.github/workflows/cicd_release-sdk.yml states in its header that it publishes "to the latest tag only" and that there is "no @next". The next dist-tag is therefore orphaned: frozen at a pre-release build with nothing updating it. Every customer who scaffolds the documented way receives a stale dev/QA pre-release, which is not a supported configuration:

npx create-next-app my-dotcms-app --example https://github.com/dotCMS/core/tree/main/examples/nextjs

The latest pins are a different failure: they float ahead of the customer's server, producing the recurring FieldUndefined GraphQL skew tickets.

B3. Examples on LTS release branches pin "latest". examples/nextjs/package.json on release-25.07.10_lts_v12 and _v16 pins "latest" for all five packages, resolving today to 26.8.3-1. The LTS-branch example therefore fails against its own LTS server, removing the last working escape hatch for LTS customers.

Customer impact

Helpdesk #38677 (25.07.10 LTS) is live. The customer scaffolded from the main example and got a build whose hardcoded DotCMSPage GraphQL fragment requests numberContents, styleEditorSchemas, lockedBy, lockedByName and layout metadata, none of which exist on their server. They reported trying "all available versions" without success, which B2 explains: the scaffold kept handing them the next tag. Prior instances of the same class: #36678, #37710, #38038.

The SDK's DotCMSPage fragment field list is hardcoded in buildPageQuery, and the graphql.page option only appends a ClientPage fragment, so customers cannot trim the query as a workaround. Pinning is the only mitigation, which is what makes B1 and B2 severe.

Steps to Reproduce

Defect A — version string mismatch:

  1. curl -s https://registry.npmjs.org/@dotcms/client/26.8.3-1 | jq -r .version26.8.3-1
  2. Download and extract that same tarball, then read its package.json"version": "26.08.03-01"
  3. curl -s https://registry.npmjs.org/@dotcms/react/26.8.3-1 | jq -r '.dependencies["@dotcms/client"]'26.08.03-01
  4. Confirm no such version is published: it is absent from the versions map of https://registry.npmjs.org/@dotcms/client

Defect B1 — a customer pin is silently overridden (yarn and pnpm only):

mkdir t && cd t
cat > package.json <<'JSON'
{"name":"t","version":"1.0.0","private":true,
 "dependencies":{"@dotcms/client":"1.2.0","@dotcms/react":"1.2.0",
                 "@dotcms/uve":"1.2.0","@dotcms/types":"1.2.0"}}
JSON
  • With yarn: yarn install, then check node_modules/@dotcms/react/node_modules/@dotcms/client/package.json → a second copy exists, reporting 26.08.03-01
  • With pnpm: pnpm install, then ls -l node_modules/.pnpm/@dotcms+react@1.2.0*/node_modules/@dotcms/client → symlink points at @dotcms+client@26.8.3-1
  • With npm: npm ls @dotcms/client --all → deduped to 1.2.0; not affected

Defect B2 — the scaffold delivers an orphaned pre-release:

  1. npx create-next-app my-app --example https://github.com/dotCMS/core/tree/main/examples/nextjs
  2. cd my-app && npm install && npm ls @dotcms/client
  3. Observe 26.7.27-1-next.2448, a pre-release frozen on 2026-07-29, rather than the current 26.8.3-1

Expected: a scaffolded example installs a published, supported release matching the branch it came from; a customer's explicit pin is honoured on every package manager; and an installed package reports the version it was published as.

Actual: the scaffold installs an orphaned pre-release, pins are overridden on yarn and pnpm, and installed packages report a nonexistent version.

Acceptance Criteria

Defect A — version strings

  • The release pipeline writes release versions in a form that is valid semver and identical to what the registry advertises (26.8.3-1, not 26.08.03-01)
  • For a newly published package, the tarball-internal package.json version matches the registry metadata version exactly
  • Injected @dotcms/* dependency pins use that same exact string
  • Resolution no longer depends on loose-mode semver coercion; a strict semver parse of every published @dotcms/* version and internal dependency range succeeds

Defect B — floating tags

  • No core-web/libs/sdk/*/package.json declares a floating specifier (latest, next, *) for an @dotcms/* entry in dependencies or peerDependencies
  • The release pipeline rewrites dependencies, not only peerDependencies, for SDK libs; bump-sdk-versions.js is updated or retired accordingly (its bumpVersion regex only accepts -alpha.N and is already dead code for the CalVer scheme)
  • examples/nextjs, examples/vuejs, examples/angular, examples/astro on main pin exact published versions instead of next or latest, and the pipeline updates those pins when publishing
  • The next dist-tag is either resumed by the pipeline or removed from npm, so no documented path resolves to an orphaned tag
  • Examples on maintained LTS branches pin a version compatible with that LTS line (for release-25.07.10_lts* this is 1.2.0, the newest release whose page query validates against that schema)
  • CI fails the build if any core-web/libs/sdk/*/package.json or examples/*/package.json reintroduces a floating @dotcms/* specifier

Verification

  • Scaffolding each example from main, installing, and running npm ls @dotcms/client yields exactly one copy at the expected published release
  • Scaffolding examples/nextjs from release-25.07.10_lts_v16 against a 25.07.10 LTS server renders a page with no FieldUndefined errors
  • Installing a pinned @dotcms/react under npm, yarn and pnpm yields exactly one @dotcms/client at the pinned version in all three

Follow-up for support docs (published 1.x cannot be fixed retroactively): record that any recommendation to pin a 1.x version must include overrides (npm), resolutions (yarn) or pnpm-workspace.yaml overrides (pnpm 10+), plus a verification step. Note specifically that pnpm.overrides inside package.json is silently ignored by pnpm 11 — I verified it does not take effect and the override must go in pnpm-workspace.yaml.

dotCMS Version

  • Defect A: current published packages, 26.7.21-1 onward (verified on 26.8.3-1)
  • Defect B1: source on main; published @dotcms/react and @dotcms/angular 1.0.6 through 1.7.0
  • Defect B2/B3: main, release-25.07.10_lts_v12, release-25.07.10_lts_v16
  • Reported against server 25.07.10 LTS; the packaging defects themselves are server-version independent
  • Verified with npm 11, yarn 1.22.21, pnpm 11.7.0

Severity

High - Major functionality broken

Links

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.