mastra-ai/mastra · error · UnsupportedStdinCloseError

${this.constructor.name} does not support closing stdin

Error message

${this.constructor.name} does not support closing stdin

What it means

ProcessHandle.closeStdin() default implementation throws UnsupportedStdinCloseError because the provider's transport has no stdin-close primitive. Providers only override closeStdin when their transport actually supports signaling EOF on stdin.

Source

Thrown at packages/core/src/workspace/sandbox/process-manager/process-handle.ts:211

  abstract readonly pid: string;
  /** Exit code, undefined while the process is still running */
  abstract readonly exitCode: number | undefined;
  /** The command that was spawned (set by the process manager) */
  command?: string;
  /** Kill the running process (SIGKILL). Returns true if killed, false if not found. */
  abstract kill(): Promise<boolean>;
  /** Send data to the process's stdin */
  abstract sendStdin(data: string): Promise<void>;

  /**
   * Close the process's stdin, signaling EOF.
   *
   * Providers that cannot close stdin throw {@link UnsupportedStdinCloseError}.
   * The default implementation is the unsupported case, so providers only
   * override this when their transport exposes a stdin close primitive.
   */
  async closeStdin(): Promise<void> {
    throw new UnsupportedStdinCloseError(`${this.constructor.name} does not support closing stdin`);
  }

  /**
   * Wait for the process to finish and return the result.
   *
   * Optionally pass `onStdout`/`onStderr` callbacks to stream output chunks
   * while waiting. The callbacks are automatically removed when `wait()`
   * resolves, so there's no cleanup needed by the caller.
   *
   * Optionally pass an `abortSignal` to couple the blocking wait to a caller
   * lifetime: on abort the process is killed (mirroring the spawn-time
   * `abortSignal` convention in {@link CommandOptions}), which lets the wait
   * settle with the killed process's result instead of blocking forever.
   *
   * Subclasses implement `wait()` with platform-specific logic; the base
   * constructor wraps it to handle the optional streaming callbacks.
   */
  async wait(_options?: {

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Feature-detect: only call closeStdin if the provider documents/advertises support.
  2. Wrap in try-catch for UnsupportedStdinCloseError and fall back to kill() or wait().
  3. Restructure the interaction to not require EOF (e.g. length-prefixed protocol, explicit exit command).
  4. If you own the provider, override closeStdin() with the transport's native close primitive.

Example fix

// before
await handle.write(input);
await handle.closeStdin();
// after
await handle.write(input);
try {
  await handle.closeStdin();
} catch (e) {
  if (e.name !== 'UnsupportedStdinCloseError') throw e;
  // provider cannot signal EOF; rely on protocol or kill()
}
Defensive patterns

Strategy: try-catch

Validate before calling

const supportsStdinClose = typeof (provider.prototype ?? provider).closeStdin === 'function' &&
  !/UnsupportedStdinCloseError/.test((provider.prototype ?? provider).closeStdin.toString());

Type guard

function supportsStdinClose(h: object): h is { closeStdin(): Promise<void> } {
  return typeof (h as any).closeStdin === 'function' &&
    !/UnsupportedStdinCloseError/.test((h as any).closeStdin.toString());
}

Try / catch

await handle.write(input);
try {
  await handle.closeStdin();
} catch (e) {
  if (e.name !== 'UnsupportedStdinCloseError') throw e;
  await handle.kill(); // or rely on protocol-level termination
}

Prevention

When it happens

Trigger: Calling processHandle.closeStdin() (often via the `writer` helper when done writing) on a process spawned by a provider that doesn't support closing stdin — e.g. certain remote/exec-based sandbox transports.

Common situations: Using a generic interactive-process helper (write then closeStdin) across sandbox providers, one of which lacks stdin close; expecting closeStdin to terminate the process instead of using kill/wait.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/22292c63689a5931. Report an issue: GitHub.