本文へ移動
cccskills
無料GitHub で公開

docker-node-version-compat-modules

Fix Node.js CLI tools crashing inside Docker containers when host-installed node_modules require a newer Node version than the container provides. Use when: (1) "SyntaxError: Invalid regular expression flags" with /v flag in string-width or similar packages, (2) node_modules installed on host with Node 20+ but container has Node 18, (3) `npm install` with file: protocol creates symlinks that break inside Docker, (4) pnpm workspace packages become broken symlinks in containers. Covers version detection, Node binary mounting, and npm install strategies for cross-version compatibility.

インストール方法を見る

含まれるファイル(1)

  • SKILL.md5.8 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Docker Node Version Compatibility for Mounted node_modules

Problem

When mounting host-installed node_modules into a Docker container, packages may require a newer Node.js version than what's available in the container. This causes cryptic runtime errors (not install-time errors) because npm doesn't enforce engines constraints by default.

Context / Trigger Conditions

  • Primary symptom: SyntaxError: Invalid regular expression flags on the /v flag (Unicode Sets, requires Node 20+)
  • Affected packages: string-width@8.x, ink@6.x, and their dependents
  • Scenario: Host has Node 20+, Docker container has Node 18 (common in SWE-bench images)
  • Also triggers when: npm install with file: protocol creates symlinks to host paths that don't exist inside the container
  • Also triggers when: pnpm workspace @scope/pkg entries are symlinks to ../../../../workspace/packages/pkg — broken inside Docker

Root Cause

  1. npm doesn't enforce engines by default: Even with --engine-strict, it only checks direct dependencies, not transitive ones. Packages like string-width@8.x declare "engines": {"node": ">=20"} but npm happily installs them on any Node version.

  2. file: protocol creates symlinks: npm install file:../path creates a symlink in node_modules/ pointing to the host path. Inside Docker, that host path doesn't exist.

  3. pnpm workspace symlinks: In pnpm monorepos, node_modules/@scope/pkg is a symlink to ../../packages/pkg. These are relative to the workspace root, not the mount point.

Diagnostic Steps

CRITICAL: Before attempting any fix, verify these first:

  1. Check Node version in the target container:

    docker run --rm <image> node --version
    
  2. Check if the tool actually worked before (don't assume — read the trajectory/logs):

    # Look for actual command outputs, not just references in prompts
    grep '"returncode": 0' trajectory.json | grep grafema
    
  3. Check for symlinks in mounted node_modules:

    docker exec <container> bash -c "ls -la /opt/node_modules/@scope/"
    

Solutions

Solution A: Mount Node 20+ Binary (Recommended)

Download a Node.js binary for Linux and mount it alongside node_modules:

# Download once
curl -sL https://nodejs.org/dist/v20.18.0/node-v20.18.0-linux-x64.tar.xz | \
  tar -xJ -C /path/to/node20 --strip-components=1

# Mount and use in container
docker run -v /path/to/node20:/opt/node20:ro \
           -v /path/to/node_modules:/opt/modules:ro \
           <image> bash -c "
  export PATH=/opt/node20/bin:\$PATH
  # Create wrapper script for the CLI tool
  echo '#!/bin/bash' > /usr/local/bin/mytool
  echo 'exec /opt/node20/bin/node /opt/modules/.bin/mytool \"\$@\"' >> /usr/local/bin/mytool
  chmod +x /usr/local/bin/mytool
"

Solution B: Install with Version Constraints

Install inside a container matching the target Node version with overrides:

docker run --rm -v /path/to/install:/install node:18 bash -c '
  cd /install
  npm install --engine-strict  # Will fail if deps need Node 20+
'

If this fails (because core deps like ink require Node 20+), Solution A is the only option.

Solution C: pnpm pack + npm install (Flat Layout)

For pnpm monorepos, create tarballs first to eliminate workspace symlinks:

# On host (resolves workspace:* protocol)
pnpm -C packages/cli pack --pack-destination /tmp/packs

# Install from tarballs (creates real directories, not symlinks)
# Do this inside a Docker container matching target Node version
docker run --rm -v /tmp/packs:/packs:ro -v /path/to/install:/install node:20 bash -c '
  cd /install
  cat > package.json << EOF
  {"dependencies": {"@scope/cli": "file:/packs/cli-1.0.0.tgz"}}
  EOF
  npm install
'

Important: Use pnpm pack (not npm pack) to resolve workspace:* protocol.

Anti-Patterns

  1. Don't dereference symlinks with cp -RL: This copies files but npm still resolves the latest dependency versions, which may require newer Node.

  2. Don't use pnpm deploy for Docker mounts: It creates .pnpm/ layout with internal symlinks that cause ESM resolution errors in some Node versions.

  3. Don't debug Docker setup without checking if it ever worked: Verify in actual trajectory outputs, not by counting keyword references in prompts/logs.

  4. Don't try multiple fixes in sequence without diagnosing: Check Node version compatibility FIRST before attempting any fix.

Verification

# Test the full command, not just the binary path
docker exec <container> bash -c "export PATH=/opt/node20/bin:\$PATH; mytool --version"

# Test a command that exercises dependency loading (not just the entry point)
docker exec <container> bash -c "export PATH=/opt/node20/bin:\$PATH; mytool impact 'someFunction'"

Notes

  • SWE-bench Docker images use different Node versions: axios=Node 20, preact=Node 18
  • grafema overview may work while grafema impact fails because they load different modules
  • The /v regex flag (Unicode Sets) is the most common Node 20 breakage point
  • ink (terminal UI framework) switched to Node 20+ requirement starting from v6.0.0

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Fix Elixir/Erlang AST processing bugs in Grafema beam-analyzer. Use when: (1) Elixir parser returns MODULE node but 0 functions/calls — body nesting issue, (2) Erlang parser crashes with "cannot convert list to string" on OTP 26+ — location format changed from integer to keyword list, (3) pipe operator |> creates spurious CALL nodes instead of desugared function calls — clause ordering bug, (4) multi-module .ex files return only the first module — missing __block__ handler, (5) installing Erlang/Elixir on macOS with outdated Xcode/Clang.

日本語の概要は準備中です。原文の説明を表示しています。

Disentinel/grafema362026年8月24日 更新

Fires after fixing any non-trivial bug or regression. Asks: could the graph have caught this as a guarantee? Pairs with reflection-in-and-on-action — picks up after "earliest catchable signal" and asks the next question: was that signal expressible in graph? Triggers: (1) after any non-trivial bug fix is verified working, (2) after a regression report (something used to work, broke), (3) during step 6 (knowledge extraction) of the workflow, (4) when reflection-on-action surfaces a "would have been catchable" signal. Outcome is a triage decision (graph-reachable? rule expressible? rule sound?) and either a draft Linear issue + guarantee proposal, or a recorded "graph capability gap" note. Never auto-creates guarantees.

日本語の概要は準備中です。原文の説明を表示しています。

Disentinel/grafema362026年8月24日 更新

Fix Playwright automation failures against code-server (VS Code in browser). Use when: (1) trust dialog blocks all clicks — "monaco-dialog-modal-block intercepts pointer events", (2) button:has-text() finds wrong buttons behind modal dialog, (3) keyboard shortcuts don't work — Meta vs Control inconsistency, (4) VS Code extension activity bar icon not found by aria-label, (5) panels show placeholder text despite extension being "Connected". Covers trust dialog dismissal, keyboard shortcut hybrid mode, extension panel selectors, and database connection verification for Grafema extension.

日本語の概要は準備中です。原文の説明を表示しています。

Disentinel/grafema362026年8月24日 更新

Systematic methodology for achieving 100% backward dataflow reachability in a new language. Create gauntlet fixture, write trace, diagnose gaps, fix analyzer/algorithm, iterate to 100%. Language-agnostic process. Use when: (1) adding a new language to Grafema, (2) auditing dataflow coverage for existing language, (3) user says "/dataflow-gauntlet".

日本語の概要は準備中です。原文の説明を表示しています。

Disentinel/grafema362026年8月24日 更新

Fix docker exec hanging when starting background processes (servers, daemons) inside containers. Use when: (1) docker exec never returns despite using & or nohup, (2) background server started in container causes docker exec to hang indefinitely, (3) env_startup_command in SWE-bench or similar frameworks times out, (4) setsid/disown needed for proper process detachment in Docker. Root cause: docker exec tracks ALL processes in the exec session, not just the top-level PID.

日本語の概要は準備中です。原文の説明を表示しています。

Disentinel/grafema362026年8月24日 更新

Fix intermittent `{:no_translation, :unicode, :latin1}` crashes in Elixir escript daemons that use length-prefixed framed IPC on stdin/stdout. Use when: (1) daemon worker crashes only on some input files, usually ones with non-ASCII bytes (kanji, cyrillic, emoji); (2) error surfaces as `Protocol error` from daemon's error branch or as garbled frame-length bytes seen by the orchestrator/client side; (3) standalone one-shot mode works fine on the same input but multi-request daemon mode fails; (4) `IO.binread(:stdio, N)` returns `{:error, {:no_translation, :unicode, :latin1}}` despite the "bin" prefix suggesting it should be encoding-agnostic. Root cause is the escript default `:standard_io` encoding — it's `:unicode`, and `IO.binread` still routes through the io_server, which translates bytes to codepoints and errors when raw binary frames contain invalid UTF-8 sequences.

日本語の概要は準備中です。原文の説明を表示しています。

Disentinel/grafema362026年8月24日 更新

Disentinel のスキルをすべて見る

このスキルの問題を報告する