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
- Always pipe content: `echo "$body" | ecc memory save --title x --stdin`, or redirect a file: `ecc memory save --title x --stdin < body.md`.
- Prefer `--body-file <path>` for file sources; it avoids the stdin readiness problem entirely.
- Ensure the producer closes stdin so the loop sees EOF (bytesRead===0) rather than indefinite EAGAIN.
- 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
- Prefer --body-file for file sources; reserve --stdin for genuine pipes.
- Always close stdin from the producer so the loop sees EOF (bytesRead===0).
- In CI, redirect `</dev/null` explicitly if no input, or pipe content.
- Document the 5s retry budget so callers know slow producers need pre-buffering.
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
- ${label} must contain valid UTF-8 text.
- Failed to create directory '${dirPath}': ${err.message}
- memory body is too large (maximum ${maxBytes} bytes).
- Failed to read ${label}: ${error.message}
- Failed to parse ${label} at ${filePath}: ${error.message}
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/7958b6002d28ae52.
Report an issue: GitHub.