Skip to content

fix(engines): drop the <26 upper bound so Node 26 hits the guard, not a silent downgrade - #87

Merged
giancarloerra merged 1 commit into
mainfrom
fix/node26-guard-delivery
Jul 24, 2026
Merged

fix(engines): drop the <26 upper bound so Node 26 hits the guard, not a silent downgrade#87
giancarloerra merged 1 commit into
mainfrom
fix/node26-guard-delivery

Conversation

@giancarloerra

@giancarloerra giancarloerra commented Jul 22, 2026

Copy link
Copy Markdown
Owner

The problem

A user pilot on macOS (Homebrew default Node 26) found SocratiCode indexes nothing and eventually errors with fetch failed / UND_ERR_INVALID_ARG: invalid onError method. Root cause is a two-part trap, both verified on a real Node 26.5.0 runtime:

  1. Delivery. The plugin (and every README/extension example) launches the server with a bare npx -y socraticode. A bare package name is a version range (*), not the latest tag, and npm engine-filters a range. The engines.node: >=18.0.0 <26.0.0 cap (added in c23120e to "protect" Node 26 users) therefore excludes every guard-bearing version (v1.8.11–v1.9.0) on Node 26, and npm silently resolves to v1.8.10 — the last version that predates both the cap and the runtime guard.
    • Verified with npm's own resolver against the live registry: pickManifest("*", node=26.5.0)1.8.10; pickManifest("latest", node=26.5.0) → 1.9.0. And on real Node 26.5.0, npm install socraticode --dry-run → "add socraticode 1.8.10".
  2. Runtime. v1.8.10 boots (no guard), passes the plain-fetch /healthz probe (the undici bug is dispatcher-specific), then dies on the first qdrant client call. codebase_index is fire-and-forget, so it returns an optimistic "indexing started" ack and stores nothing; the failure surfaces only on the next codebase_status/codebase_search as a raw undici error that reads like an env/npx-cache quirk.

So the cap meant to protect Node 26 users is exactly what hides the loud "use Node 22" guard from them and serves the guardless build instead.

The fix (Tier 1: make the failure honest)

  • Remove the <26.0.0 upper bound (engines.node back to >=18.0.0). With no upper bound, a bare install resolves to the guarded version on Node 26. Verified against the live packument with a simulated post-fix release: * at node 26.5.0 → the guarded version → the runtime guard fires exit(1) with "Use Node 22.x" instead of silently downgrading.
  • Keep the runtime guard as the sole gate, and correct its comment: the causality was inverted. It is qdrant's own bundled undici 6 Agent rejecting the handler that Node 26's undici 8 builds (onError renamed to onResponseError), not Node rejecting the v6 Agent. The comment now also warns future maintainers never to re-add an engines upper bound (it is the misrouting mechanism).

This does not make Node 26 work (upstream qdrant-js #134 / #123 / #128 are still open); it converts silent data loss into a loud, actionable refusal for every consumer of the bare name.

Why not pin the manifests to @latest

Removing the cap fixes bare-name resolution systemically, for all ~15 npx -y socraticode references (README, extension, gemini config, both mcp.json). Pinning only the two manifests would be redundant once this ships and would leave the docs inconsistent, and @latest forces a per-launch registry check on every user. So the one-line engines change is the smaller, complete fix.

Type of change

  • Bug fix (non-breaking; converts a silent failure into a loud, correct refusal)

Testing

  • npx tsc --noEmit clean, npm run lint (biome) clean, npm run build succeeds (guard still compiled into dist/index.js)
  • Unit tests: 939/939 pass
  • CodeRabbit local review: no findings
  • Resolver proof (npm-pick-manifest vs live packument): pre-fix *@node26 → 1.8.10; post-fix *@node26 → guarded version
  • Node 26 behavior confirmed by two independent agents on a SHA-verified node-v26.5.0: bare → 1.8.10 (silent), @latest → 1.9.0 (guard fires)

Refs #82

Summary by CodeRabbit

  • Compatibility

    • Updated the supported Node.js version range to include Node.js 26 and newer versions.
  • Documentation

    • Clarified Node.js runtime compatibility behavior and startup safeguards.

… a silent downgrade

The engines cap added in c23120e to protect Node 26+ users is self-defeating.
The plugin (and the README/extension examples) launch the server with a bare
`npx -y socraticode`, which npm resolves as a version *range*. npm
engine-filters a range, so on Node 26 the `<26.0.0` bound excludes every
guard-bearing version (1.8.11-1.9.0) and silently resolves to 1.8.10 -- the
last version that predates BOTH the cap and the runtime guard. 1.8.10 then
boots on Node 26, passes the plain-fetch health probe, and dies on the first
qdrant client call (UND_ERR_INVALID_ARG: invalid onError method), storing
nothing while codebase_index reports an optimistic background ack. The loud
'use Node 22' guard lives only in the versions npm refuses to serve.

Removing the upper bound makes a bare install resolve to the guarded version on
Node 26 (verified against the live packument: '*' at node 26.5.0 -> the guarded
release), converting silent data loss into the loud, actionable refusal. The
runtime guard in src/index.ts remains the sole gate; its comment is corrected
(the causality was inverted: qdrant's bundled undici 6 rejects the handler that
Node 26's undici 8 builds, not Node rejecting the v6 Agent) and now warns
against ever re-adding an engines upper bound.

Does not make Node 26 work (upstream qdrant-js #134/#123/#128 still open); it
makes the failure honest.

Refs #82
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0a5322a2-c816-4ac0-ae5e-8db9abab3cba

📥 Commits

Reviewing files that changed from the base of the PR and between d95a52f and 6d6a298.

📒 Files selected for processing (2)
  • package.json
  • src/index.ts

📝 Walkthrough

Walkthrough

The Node.js engine requirement now permits versions 18 and newer. Runtime guard documentation explains the Node 26+ undici incompatibility and import timing; the guard logic itself is unchanged.

Changes

Node 26 compatibility

Layer / File(s) Summary
Engine constraint and runtime guard policy
package.json, src/index.ts
The Node.js engine upper bound is removed, while comments document the existing Node 26+ startup guard and undici dispatcher hook mismatch.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Possibly related PRs

Suggested reviewers: shaitourchin

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: removing the Node 26 engine cap so the runtime guard is reached.
Description check ✅ Passed The description follows the template with Summary, Changes, Type of change, Testing, Checklist, and Related issues sections filled in.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/node26-guard-delivery

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Checkov (3.3.8)
package.json

Traceback (most recent call last):
File "/usr/local/bin/checkov", line 2, in
from checkov.main import Checkov
ModuleNotFoundError: No module named 'checkov'


Comment @coderabbitai help to get the list of available commands.

@giancarloerra giancarloerra self-assigned this Jul 24, 2026
@giancarloerra
giancarloerra merged commit 9b68f45 into main Jul 24, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant