laurent22/joplin · error · Error

Reading commands from standard input is only available in…

Error message

Reading commands from standard input is only available in CLI mode.

What it means

The 'batch' command reads a command script from a file or, when the path is '-', from standard input. Because the interactive terminal GUI also reads from stdin, reading commands from stdin conflicts with the GUI; if app().hasGui() is true the command throws 'Reading commands from standard input is only available in CLI mode.'. The message is i18n-translated.

Solutions

  1. Write the commands to a file and run 'batch <file>' instead of stdin.
  2. Run in pure CLI mode (no terminal GUI) when piping via stdin.
  3. Use the data API or a single 'exec' call instead of batch stdin in GUI mode.

Example fix

# before
#   echo 'ls' | joplin batch -    # inside terminal GUI -> throws
# after
#   printf 'ls\n' > /tmp/cmds.txt && joplin batch /tmp/cmds.txt
Defensive patterns

Strategy: validation

Validate before calling

if (filePath === '-' && app().hasGui()) {
  throw new Error('Stdin batch input requires CLI mode; write to a file instead.');
}

Prevention

When it happens

Trigger: Running 'batch -' (or piping commands into stdin) while inside the interactive terminal GUI, where stdin is already owned by the GUI input loop.

Common situations: Piping a command list ('echo ... | joplin batch -') in a mode where the terminal GUI is active; running batch from a keybinding inside the GUI.

Related errors


AI-assisted analysis of laurent22/joplin@2654b33620 (2026-08-12). Data as JSON: /api/errors/b46fdcea483b5801. Report an issue: GitHub.

Appendix: source

Thrown at packages/app-cli/app/command-batch.ts:45

	public description() {
		return _('Runs the commands contained in the text file. There should be one command per line.');
	}

	private streamCommands_ = async function*(filePath: string) {
		const processLines = function*(lines: string) {
			const commandLines = splitCommandBatch(lines);

			for (const command of commandLines) {
				if (!command.trim()) continue;
				yield splitCommandString(command.trim());
			}
		};

		if (filePath === '-') { // stdin
			// Iterating over standard input conflicts with the CLI app's GUI.
			if (app().hasGui()) {
				throw new Error(_('Reading commands from standard input is only available in CLI mode.'));
			}

			for await (const lines of iterateStdin('command> ')) {
				yield* processLines(lines);
			}
		} else {
			const data = await readFile(filePath, 'utf-8');
			yield* processLines(data);
		}
	};

	public async action(options: Options) {
		let lastError;
		for await (const command of this.streamCommands_(options['file-path'])) {
			try {
				await app().refreshCurrentFolder();
				await app().execCommand(command);
			} catch (error) {

View on GitHub (pinned to 2654b33620)