affaan-m/ECC · error · Error

Standard input remained unavailable after ${maxRetryWaitMs}m

Error message

Standard input remained unavailable after ${maxRetryWaitMs}ms.

What it means

readBoundedStdin reads stdin via synchronous fs.readSync in a loop. When readSync throws EAGAIN/EWOULDBLOCK/EINTR (stdin not ready, e.g. a non-blocking pipe with no data yet), it waits retryDelayMs and retries, burning a budget of maxRetryWaitMs (default MAX_STDIN_RETRY_WAIT_MS = 5000). If the budget is exhausted before stdin yields data, it throws. It is not thrown for a clean EOF (bytesRead===0 ends the loop normally) — only for stdin that stays unavailable.

Source

Thrown at scripts/memory.js:175

    && retryOptions.maxRetryWaitMs >= 0
    ? retryOptions.maxRetryWaitMs
    : MAX_STDIN_RETRY_WAIT_MS;
  const wait = typeof retryOptions.wait === 'function'
    ? retryOptions.wait
    : waitForStdinRetry;
  const chunks = [];
  let total = 0;
  let remainingRetryWaitMs = maxRetryWaitMs;
  while (total <= maxBytes) {
    const buffer = Buffer.alloc(Math.min(64 * 1024, maxBytes + 1 - total));
    let bytesRead;
    try {
      bytesRead = fs.readSync(0, buffer, 0, buffer.length, null);
    } catch (error) {
      const retryable = ['EAGAIN', 'EWOULDBLOCK', 'EINTR'].includes(error?.code);
      if (!retryable) throw error;
      if (remainingRetryWaitMs < retryDelayMs) {
        throw new Error(
          `Standard input remained unavailable after ${maxRetryWaitMs}ms.`
        );
      }
      wait(retryDelayMs);
      remainingRetryWaitMs -= retryDelayMs;
      continue;
    }
    if (bytesRead === 0) break;
    chunks.push(buffer.subarray(0, bytesRead));
    total += bytesRead;
  }
  if (total > maxBytes) {
    throw new Error(`memory body is too large (maximum ${maxBytes} bytes).`);
  }
  return decodeUtf8(Buffer.concat(chunks, total), 'memory body from standard input');
}

function readBody(options) {

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Always pipe content: `echo "$body" | ecc memory save --title x --stdin`, or redirect a file: `ecc memory save --title x --stdin < body.md`.
  2. Prefer `--body-file <path>` for file sources; it avoids the stdin readiness problem entirely.
  3. Ensure the producer closes stdin so the loop sees EOF (bytesRead===0) rather than indefinite EAGAIN.
  4. If you must use --stdin with a slow producer, raise the budget by patching MAX_STDIN_RETRY_WAIT_MS or feeding input faster.

Example fix

# before — no data on stdin, hangs then throws
ecc memory save --title "notes" --stdin

# after — pipe the body so stdin is ready immediately
echo "Release went out at 10:00." | ecc memory save --title "notes" --stdin

# or use a file source instead
 ecc memory save --title "notes" --body-file ./notes.md
Defensive patterns

Strategy: retry

Validate before calling

// Before using --stdin, ensure there is actually a producer on the other end.
const fs = require('fs');
function stdinLooksReady() {
  try { return fs.fstatSync(0).isFIFO() || !process.stdin.isTTY; }
  catch { return false; }
}
if (options.stdin && !stdinLooksReady()) {
  throw new Error('--stdin given but no piped input detected; pipe content or use --body-file.');
}

Try / catch

try { body = readBoundedStdin(MAX_BODY_BYTES); }
catch (err) {
  if (/remained unavailable/.test(err.message)) {
    // Fall back to a file source if one was provided, else surface clearly.
    body = options.bodyFile ? readRegularTextFile(options.bodyFile) : null;
    if (!body) throw err;
  } else throw err;
}

Prevention

When it happens

Trigger: Invoking `memory save --stdin` with no piped input and stdin connected to a non-blocking fd that never produces data. Running under a harness that connects a TTY-like fd marked non-blocking. A slow upstream producer that takes >5s to start; or a pipe that was opened but never written to and never closed.

Common situations: Calling the CLI interactively with --stdin and forgetting to pipe/redirect. A parent process that spawns memory with --stdin but never writes or closes the pipe. CI where stdin is /dev/null but opened in non-blocking mode that keeps returning EAGAIN instead of EOF.

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/7958b6002d28ae52. Report an issue: GitHub.