gitbutlerapp/gitbutler · error

stdin was empty. To intentionally delete the conflicted…

Error message

stdin was empty. To intentionally delete the conflicted region, pass --file with an empty file.

What it means

Thrown by apply_resolutions when stdin yields empty content and stdin was not a promptable TTY. Because an empty replacement could be intentional (deleting the conflicted region), the error tells the user to make that intent explicit with an empty --file instead of accidentally piping nothing.

Solutions

  1. If deletion is intended, pass `--file` pointing at an empty file.
  2. Provide real content via --file or stdin.
  3. Take a side with --ours/--theirs/--ai.

Example fix

// before
cat empty-or-failed-output | but resolve abc123 file.rs
// after
: > empty.txt && but resolve abc123 file.rs --file empty.txt
Defensive patterns

Strategy: validation

Validate before calling

let content = '';
if (!args.file) { content = await readStdin(); }
if (!args.file && content === '' && !args.intentionalDelete) {
  throw new Error('stdin empty; pass --file with an empty file to delete the region');
}

Try / catch

try { await butResolve(commit, path) } catch (e) { if (String(e).includes('stdin was empty')) { writeFileSync('empty.txt',''); await butResolve(commit, path, ['--file','empty.txt']); } }

Prevention

When it happens

Trigger: `but resolve <commit> <path>` run non-interactively with an empty stdin (e.g. nohup, CI, or an empty pipe) and no --file/--ours/--theirs/--ai.

Common situations: CI pipelines where stdin is /dev/null; a failed upstream command produced no output but the pipe still ran.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/2646b31371e88f07. Report an issue: GitHub.

Appendix: source

Thrown at crates/but/src/command/legacy/resolve.rs:920

        HunkResolution::Ours
    } else if theirs {
        HunkResolution::Theirs
    } else if ai {
        HunkResolution::Ai
    } else {
        let content = match &file {
            Some(file) => std::fs::read_to_string(file)
                .with_context(|| format!("Failed to read {}", file.display()))?,
            None => {
                if out.can_prompt() {
                    bail!(
                        "Provide the replacement content via --file or stdin, or take a side with --ours/--theirs/--ai."
                    );
                }
                let mut content = String::new();
                std::io::Read::read_to_string(&mut read, &mut content)?;
                if content.is_empty() {
                    bail!(
                        "stdin was empty. To intentionally delete the conflicted region, pass --file with an empty file."
                    );
                }
                content
            }
        };
        HunkResolution::Content(content)
    };

    // Expand a bare path into explicit per-hunk specs; an explicit `:<N>`
    // target needs no lookup, the API validates it.
    let hunks: Vec<usize> = match (hunk, &resolution) {
        (Some(hunk), _) => vec![hunk],
        (None, _) => {
            let conflicts = but_api::resolve::commit_conflicts(ctx, commit_oid)?;
            if conflicts.files.is_empty() && conflicts.manual.is_empty() {
                bail!("Commit {commit_oid} is not conflicted.");
            }

View on GitHub (pinned to 58e5313667)