Skip to content

error: The first argument must be a Readable, a ReadableStream, or an async iterable. #1

@tfriedel

Description

@tfriedel

I tried this out and and when I run ClaudeCage I get the following errors.
I asked claude code to analyse and fix it and it's solution was to remove bun and use node.js instead.

  [ INFO ][2025.08.11 22:43:48]: Found RunImage config: '/home/thomas/projects/ClaudeCage/ClaudeCage.rcfg'
  [ WARNING ][2025.08.11 22:43:48]: Host PIDs are unshared!
  [ WARNING ][2025.08.11 22:43:48]: Host /tmp is unshared!
  [ INFO ][2025.08.11 22:43:48]: Autorun mode: claude
  [ INFO ][2025.08.11 22:43:48]: Setting temporary $HOME to: '/home/thomas'
  [ WARNING ][2025.08.11 22:43:48]: Hostname is unshared!
  [ WARNING ][2025.08.11 22:43:48]: Users are unshared!
  [ INFO ][2025.08.11 22:43:48]: Bind: '/home/thomas/.claude.json' -> '/home/thomas/.claude.json'
  [ INFO ][2025.08.11 22:43:48]: Bind: '/home/thomas/.claude' -> '/home/thomas/.claude'
  [ INFO ][2025.08.11 22:43:48]: Bind: '/home/thomas/projects/ClaudeCage' -> '/home/thomas/projects/ClaudeCage'
  ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
  │                                                                                                                                       │
  │ Do you trust the files in this folder?                                                                                                │
  │                                                                                                                                       │
  │ /home/thomas/projects/ClaudeCage                                                                                                      │
  │                                                                                                                                       │
  │ Claude Code may read files in this folder. Reading untrusted files may lead Claude Code to behave in unexpected ways.                 │
  │                                                                                                                                       │
  │ With your permission Claude Code may execute files in this folder. Executing untrusted code is unsafe.                                │
  │                                                                                                                                       │
  │ https://docs.anthropic.com/s/claude-code-security                                                                                     │
  │                                                                                                                                       │
  │ ❯ 1. Yes, proceed                                                                                                                     │
  │   2. No, exit                                                                                                                         │
  │                                                                                                                                       │
  ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
     Enter to confirm · Esc to exit
  ╭───────────────────────────────────────────────────╮
  │ ✻ Welcome to Claude Code!                         │
  │                                                   │
  │   /help for help, /status for your current setup  │
  │                                                   │
  │   cwd: /home/thomas/projects/ClaudeCage           │
  ╰───────────────────────────────────────────────────╯

   Tips for getting started:

   1. Run /init to create a CLAUDE.md file with instructions for Claude
   2. Use Claude to help with file analysis, editing, bash commands and git
   3. Be as specific as you would with another engineer for the best results

  ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
  │ > Try "fix lint errors"                                                                                                               │
  ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
    ? for shortcuts
  674 | `)}),this}_outputHelpIfRequested(A){let B=this._getHelpOption();if(B&&A.find((D)=>B.is(D)))this.outputHelp(),this._exit(0,"commander.helpDisplayed","(outputHelp)")}}function jdB(A){return A.map((B)=>{if(!B.startsWith("--inspect"))return B;let
  Q,D="127.0.0.1",Z="9229",G;if((G=B.match(/^(--inspect(-brk)?)$/))!==null)Q=G[1];else if((G=B.match(/^(--inspect(-brk|-port)?)=([^:]+)$/))!==null)if(Q=G[1],/^\d+$/.test(G[3]))Z=G[3];else D=G[3];else
  if((G=B.match(/^(--inspect(-brk|-port)?)=([^:]+):(\d+)$/))!==null)Q=G[1],D=G[3],Z=G[4];if(Q&&Z!=="0")return`${Q}=${D}:${parseInt(Z)+1}`;return B})}BM8.Command=oO0});var
  vdB=E((GM8)=>{var{Argument:kdB}=Vg1(),{Command:tO0}=ydB(),{CommanderError:DM8,InvalidArgumentError:_dB}=CI1(),{Help:ZM8}=iO0(),{Option:xdB}=nO0();GM8.program=new tO0;GM8.createCommand=(A)=>new tO0(A);GM8.createOption=(A,B)=>new xdB(A,B);GM8.createArgument=(A,B)=>new
  kdB(A,B);GM8.Command=tO0;GM8.Option=xdB;GM8.Argument=kdB;GM8.Help=ZM8;GM8.CommanderError=DM8;GM8.InvalidArgumentError=_dB;GM8.InvalidOptionArgument | ... truncated
  675 | `:`
  676 | `.charCodeAt(),Q=typeof A==="string"?"\r":"\r".charCodeAt();if(A[A.length-1]===B)A=A.slice(0,-1);if(A[A.length-1]===Q)A=A.slice(0,-1);return A}import EW1 from"node:process";import oA1 from"node:path";import{fileURLToPath as py0}from"node:url";function
  zW1(A={}){let{env:B=process.env,platform:Q=process.platform}=A;if(Q!=="win32")return"PATH";return Object.keys(B).reverse().find((D)=>D.toUpperCase()==="PATH")||"Path"}var
  ItB=({cwd:A=EW1.cwd(),path:B=EW1.env[zW1()],preferLocal:Q=!0,execPath:D=EW1.execPath,addExecPath:Z=!0}={})=>{let G=A instanceof URL?py0(A):A,F=oA1.resolve(G),I=[];if(Q)YtB(I,F);if(Z)WtB(I,D,F);return[...I,B].join(oA1.delimiter)},YtB=(A,B)=>{let
  Q;while(Q!==B)A.push(oA1.join(B,"node_modules/.bin")),Q=B,B=oA1.resolve(B,"..")},WtB=(A,B,Q)=>{let D=B instanceof URL?py0(B):B;A.push(oA1.resolve(Q,D,".."))},iy0=({env:A=EW1.env,...B}={})=>{A={...A};let Q=zW1({env:A});return B.path=A[Q],A[Q]=ItB(B),A};var
  JtB=(A,B,Q,D)=>{if(Q==="length"||Q==="prototype")return;if(Q==="arguments"||Q==="caller")return;let  | ... truncated
  677 | ${B}`,KtB=Object.getOwnPropertyDescriptor(Function.prototype,"toString"),HtB=Object.getOwnPropertyDescriptor(Function.prototype.toString,"name"),ztB=(A,B,Q)=>{let D=Q===""?"":`with ${Q.trim()}()
  `,Z=CtB.bind(null,D,B.toString());Object.defineProperty(Z,"name",HtB),Object.defineProperty(A,"toString",{...KtB,value:Z})};function Gm1(A,B,{ignoreNonConfigurable:Q=!1}={}){let{name:D}=A;for(let Z of Reflect.ownKeys(B))JtB(A,B,Z,Q);return VtB(A,B),ztB(A,B,D),A}var
  UW1=new WeakMap,ny0=(A,B={})=>{if(typeof A!=="function")throw new TypeError("Expected a function");let Q,D=0,Z=A.displayName||A.name||"<anonymous>",G=function(...F){if(UW1.set(G,++D),D===1)Q=A.apply(this,F),A=null;else if(B.throw===!0)throw new Error(`Function
  \`${Z}\` can only be called once`);return Q};return Gm1(G,A),UW1.set(G,D),G};ny0.callCount=(A)=>{if(!UW1.has(A))throw new Error(`The given function \`${A.name}\` is not wrapped by the \`onetime\` package`);return UW1.get(A)};var ay0=ny0;import OtB
  from"node:process";import{constants as $tB}from"node:o | ... truncated
  678 | ${D.message}`:z,N=[L,B,A].filter(Boolean).join(`
  679 | `);if($)D.originalMessage=D.message,D.message=N;else D=new Error(N);if(D.shortMessage=L,D.command=F,D.escapedCommand=I,D.exitCode=G,D.signal=Z,D.signalDescription=C,D.stdout=A,D.stderr=B,D.cwd=V,Q!==void 0)D.all=Q;if("bufferedData"in D)delete
  D.bufferedData;return D.failed=!0,D.timedOut=Boolean(Y),D.isCanceled=W,D.killed=J&&!Y,D};var wW1=["stdin","stdout","stderr"],PtB=(A)=>wW1.some((B)=>A[B]!==void 0),ey0=(A)=>{if(!A)return;let{stdio:B}=A;if(B===void 0)return wW1.map((D)=>A[D]);if(PtB(A))throw new
  Error(`It's not possible to provide \`stdio\` in combination with one of ${wW1.map((D)=>`\`${D}\``).join(", ")}`);if(typeof B==="string")return B;if(!Array.isArray(B))throw new TypeError(`Expected \`stdio\` to be of type \`string\` or \`Array\`, got \`${typeof
  B}\``);let Q=Math.max(B.length,wW1.length);return Array.from({length:Q},(D,Z)=>B[Z])};import ytB from"node:os";var Zf=[];Zf.push("SIGHUP","SIGINT","SIGTERM");if(process.platform!=="win32")Zf.push("SIGALRM","SIGABRT","SIGVTALRM","SIGXCPU","SIGXFSZ","SIGUSR2"," | ...
  truncated

  error: The first argument must be a Readable, a ReadableStream, or an async iterable.
        at <anonymous> (/opt/bun/install/global/node_modules/@anthropic-ai/claude-code/cli.js:679:5262)
        at eA1 (/opt/bun/install/global/node_modules/@anthropic-ai/claude-code/cli.js:679:5228)
        at wm1 (/opt/bun/install/global/node_modules/@anthropic-ai/claude-code/cli.js:679:8191)
        at <anonymous> (/opt/bun/install/global/node_modules/@anthropic-ai/claude-code/cli.js:679:10089)
        at Rk0 (/opt/bun/install/global/node_modules/@anthropic-ai/claude-code/cli.js:679:10074)
        at <anonymous> (/opt/bun/install/global/node_modules/@anthropic-ai/claude-code/cli.js:680:177)
        at <anonymous> (/opt/bun/install/global/node_modules/@anthropic-ai/claude-code/cli.js:680:158)
        at G (/opt/bun/install/global/node_modules/@anthropic-ai/claude-code/cli.js:676:1336)
        at <anonymous> (/opt/bun/install/global/node_modules/@anthropic-ai/claude-code/cli.js:679:10211)
        at <anonymous> (/opt/bun/install/global/node_modules/@anthropic-ai/claude-code/cli.js:748:6053)
  674 | `)}),this}_outputHelpIfRequested(A){let B=this._getHelpOption();if(B&&A.find((D)=>B.is(D)))this.outputHelp(),this._exit(0,"commander.helpDisplayed","(outputHelp)")}}function jdB(A){return A.map((B)=>{if(!B.startsWith("--inspect"))return B;let
  Q,D="127.0.0.1",Z="9229",G;if((G=B.match(/^(--inspect(-brk)?)$/))!==null)Q=G[1];else if((G=B.match(/^(--inspect(-brk|-port)?)=([^:]+)$/))!==null)if(Q=G[1],/^\d+$/.test(G[3]))Z=G[3];else D=G[3];else
  if((G=B.match(/^(--inspect(-brk|-port)?)=([^:]+):(\d+)$/))!==null)Q=G[1],D=G[3],Z=G[4];if(Q&&Z!=="0")return`${Q}=${D}:${parseInt(Z)+1}`;return B})}BM8.Command=oO0});var
  vdB=E((GM8)=>{var{Argument:kdB}=Vg1(),{Command:tO0}=ydB(),{CommanderError:DM8,InvalidArgumentError:_dB}=CI1(),{Help:ZM8}=iO0(),{Option:xdB}=nO0();GM8.program=new tO0;GM8.createCommand=(A)=>new tO0(A);GM8.createOption=(A,B)=>new xdB(A,B);GM8.createArgument=(A,B)=>new
  kdB(A,B);GM8.Command=tO0;GM8.Option=xdB;GM8.Argument=kdB;GM8.Help=ZM8;GM8.CommanderError=DM8;GM8.InvalidArgumentError=_dB;GM8.InvalidOptionArgument | ... truncated
  675 | `:`
  676 | `.charCodeAt(),Q=typeof A==="string"?"\r":"\r".charCodeAt();if(A[A.length-1]===B)A=A.slice(0,-1);if(A[A.length-1]===Q)A=A.slice(0,-1);return A}import EW1 from"node:process";import oA1 from"node:path";import{fileURLToPath as py0}from"node:url";function
  zW1(A={}){let{env:B=process.env,platform:Q=process.platform}=A;if(Q!=="win32")return"PATH";return Object.keys(B).reverse().find((D)=>D.toUpperCase()==="PATH")||"Path"}var
  ItB=({cwd:A=EW1.cwd(),path:B=EW1.env[zW1()],preferLocal:Q=!0,execPath:D=EW1.execPath,addExecPath:Z=!0}={})=>{let G=A instanceof URL?py0(A):A,F=oA1.resolve(G),I=[];if(Q)YtB(I,F);if(Z)WtB(I,D,F);return[...I,B].join(oA1.delimiter)},YtB=(A,B)=>{let
  Q;while(Q!==B)A.push(oA1.join(B,"node_modules/.bin")),Q=B,B=oA1.resolve(B,"..")},WtB=(A,B,Q)=>{let D=B instanceof URL?py0(B):B;A.push(oA1.resolve(Q,D,".."))},iy0=({env:A=EW1.env,...B}={})=>{A={...A};let Q=zW1({env:A});return B.path=A[Q],A[Q]=ItB(B),A};var
  JtB=(A,B,Q,D)=>{if(Q==="length"||Q==="prototype")return;if(Q==="arguments"||Q==="caller")return;let  | ... truncated
  677 | ${B}`,KtB=Object.getOwnPropertyDescriptor(Function.prototype,"toString"),HtB=Object.getOwnPropertyDescriptor(Function.prototype.toString,"name"),ztB=(A,B,Q)=>{let D=Q===""?"":`with ${Q.trim()}()
  `,Z=CtB.bind(null,D,B.toString());Object.defineProperty(Z,"name",HtB),Object.defineProperty(A,"toString",{...KtB,value:Z})};function Gm1(A,B,{ignoreNonConfigurable:Q=!1}={}){let{name:D}=A;for(let Z of Reflect.ownKeys(B))JtB(A,B,Z,Q);return VtB(A,B),ztB(A,B,D),A}var
  UW1=new WeakMap,ny0=(A,B={})=>{if(typeof A!=="function")throw new TypeError("Expected a function");let Q,D=0,Z=A.displayName||A.name||"<anonymous>",G=function(...F){if(UW1.set(G,++D),D===1)Q=A.apply(this,F),A=null;else if(B.throw===!0)throw new Error(`Function
  \`${Z}\` can only be called once`);return Q};return Gm1(G,A),UW1.set(G,D),G};ny0.callCount=(A)=>{if(!UW1.has(A))throw new Error(`The given function \`${A.name}\` is not wrapped by the \`onetime\` package`);return UW1.get(A)};var ay0=ny0;import OtB
  from"node:process";import{constants as $tB}from"node:o | ... truncated
  678 | ${D.message}`:z,N=[L,B,A].filter(Boolean).join(`
  679 | `);if($)D.originalMessage=D.message,D.message=N;else D=new Error(N);if(D.shortMessage=L,D.command=F,D.escapedCommand=I,D.exitCode=G,D.signal=Z,D.signalDescription=C,D.stdout=A,D.stderr=B,D.cwd=V,Q!==void 0)D.all=Q;if("bufferedData"in D)delete
  D.bufferedData;return D.failed=!0,D.timedOut=Boolean(Y),D.isCanceled=W,D.killed=J&&!Y,D};var wW1=["stdin","stdout","stderr"],PtB=(A)=>wW1.some((B)=>A[B]!==void 0),ey0=(A)=>{if(!A)return;let{stdio:B}=A;if(B===void 0)return wW1.map((D)=>A[D]);if(PtB(A))throw new
  Error(`It's not possible to provide \`stdio\` in combination with one of ${wW1.map((D)=>`\`${D}\``).join(", ")}`);if(typeof B==="string")return B;if(!Array.isArray(B))throw new TypeError(`Expected \`stdio\` to be of type \`string\` or \`Array\`, got \`${typeof
  B}\``);let Q=Math.max(B.length,wW1.length);return Array.from({length:Q},(D,Z)=>B[Z])};import ytB from"node:os";var Zf=[];Zf.push("SIGHUP","SIGINT","SIGTERM");if(process.platform!=="win32")Zf.push("SIGALRM","SIGABRT","SIGVTALRM","SIGXCPU","SIGXFSZ","SIGUSR2"," | ...
  truncated

  error: The first argument must be a Readable, a ReadableStream, or an async iterable.
        at <anonymous> (/opt/bun/install/global/node_modules/@anthropic-ai/claude-code/cli.js:679:5262)
        at eA1 (/opt/bun/install/global/node_modules/@anthropic-ai/claude-code/cli.js:679:5228)
        at wm1 (/opt/bun/install/global/node_modules/@anthropic-ai/claude-code/cli.js:679:8191)
        at <anonymous> (/opt/bun/install/global/node_modules/@anthropic-ai/claude-code/cli.js:679:10095)
        at Rk0 (/opt/bun/install/global/node_modules/@anthropic-ai/claude-code/cli.js:679:10074)
        at <anonymous> (/opt/bun/install/global/node_modules/@anthropic-ai/claude-code/cli.js:680:177)
        at <anonymous> (/opt/bun/install/global/node_modules/@anthropic-ai/claude-code/cli.js:680:158)
        at G (/opt/bun/install/global/node_modules/@anthropic-ai/claude-code/cli.js:676:1336)
        at <anonymous> (/opt/bun/install/global/node_modules/@anthropic-ai/claude-code/cli.js:679:10211)
        at <anonymous> (/opt/bun/install/global/node_modules/@anthropic-ai/claude-code/cli.js:748:6053)

  Bun v1.2.20 (Linux x64)

Claude Code's analysis:

The issue is that Bun, while being a fast JavaScript runtime and package manager, has incomplete compatibility with Node.js APIs - specifically around stream handling. Here are the likely technical reasons:

  Stream API Differences

  1. Stream Implementation: Bun implements its own version of Node.js streams that isn't 100% compatible. The error "The first argument must be a Readable, a ReadableStream, or an async iterable" suggests Bun's stream implementation expects different types or handles
  stream creation differently than Node.js.
  2. Web Streams vs Node Streams: Bun tries to bridge Web Streams (browser standard) and Node.js Streams, but claude-code likely uses Node.js-specific stream patterns that don't translate perfectly.

  Interactive Mode Complexity

  The fact that ./ClaudeCage --help worked with Bun but interactive mode failed suggests:

  - TTY/stdin handling: Interactive mode requires complex terminal input/output stream management
  - Process streams: claude-code probably sets up process.stdin, process.stdout in ways that Bun doesn't fully emulate
  - Event loop differences: Bun's event loop implementation differs from Node.js, which can affect stream event handling

  Claude-code Specifics

  Claude-code likely uses:
  - Advanced readline/TTY manipulation for the interactive prompt
  - Complex stream piping for handling user input
  - Node.js-specific process stream behaviors

  Bun's Maturity

  While Bun is very fast and works great for many use cases, it's still relatively new compared to Node.js and doesn't have 100% API compatibility, especially for complex stream operations that interactive CLI tools require.

  This is why many complex Node.js applications still need Node.js specifically - Bun works well for simpler scripts and web applications, but advanced system interactions like interactive CLIs can expose compatibility gaps.```


● Summary of the Fix

  Problem: ClaudeCage was failing with Bun runtime error:
  error: The first argument must be a Readable, a ReadableStream, or an async iterable.
  Bun v1.2.20 (Linux x64)

  Root Cause: Bun has incomplete Node.js stream API compatibility, causing claude-code's interactive mode to fail.

  Solution: Completely removed Bun and switched to Node.js + npm

  Key Changes Made:

  1. Found the real setup script: Discovered that build.sh contains an inline setup script (lines 86-116) that was being used instead of the separate setup_in_container.sh file
  2. Updated the inline setup script in build.sh:
    - Removed all Bun installation and usage code
    - Added nodejs npm to package installation
    - Changed from $BUN_INSTALL/bin/bun install -g @anthropic-ai/claude-code to npm install -g @anthropic-ai/claude-code
    - Updated wrapper script to call /usr/bin/claude (npm's global binary) instead of Bun paths
  3. Rebuilt container with npm-only approach

  Result: ClaudeCage now works perfectly - claude-code starts in interactive mode without stream errors, showing the proper welcome screen and prompt.

  The fix worked because Node.js has native, complete stream API support that claude-code relies on, while Bun's stream implementation had compatibility gaps for complex interactive CLI applications.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions