gitbutlerapp/gitbutler · error

Found only the GitButler managed block start marker…

Error message

Found only the GitButler managed block start marker. Refusing to edit a partial managed block.

What it means

GitButler's agent-managed file block (e.g. an instructions file section) is delimited by a start and end marker. This error is thrown when only the start marker exists, so the managed block is incomplete. The tool refuses to edit a partial block rather than corrupting the file.

Solutions

  1. Open the file and restore the missing GitButler managed block end marker after the start marker.
  2. Alternatively delete the start marker entirely so upsert_managed_block appends a fresh, complete block.
  3. Re-run the upsert command after the file contains either a complete block or no markers.

Example fix

// before (broken file)
<!-- GITBUTLER-MANAGED-BLOCK:START -->
// after (fixed file)
<!-- GITBUTLER-MANAGED-BLOCK:START -->
...content...
<!-- GITBUTLER-MANAGED-BLOCK:END -->
Defensive patterns

Strategy: validation

Validate before calling

const content = fs.readFileSync(file, 'utf8');
const starts = content.split('\n').filter(l => l.includes('GITBUTLER-MANAGED-BLOCK:START')).length;
const ends = content.split('\n').filter(l => l.includes('GITBUTLER-MANAGED-BLOCK:END')).length;
if (starts !== ends || starts === 0) throw new Error(`Unbalanced managed-block markers in ${file}: ${starts} start, ${ends} end`);

Prevention

When it happens

Trigger: Calling upsert_managed_block (via `but agent` file upserts) on a file where find_line_anchored finds MANAGED_BLOCK_START but no MANAGED_BLOCK_END — typically after a user manually deleted the end marker or a truncated write/merge removed it.

Common situations: Users hand-editing their AGENT.md/instructions file and deleting the closing marker; merge conflicts or partial checkouts dropping the block tail; copy-pasting only the header marker into a new file.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at crates/but/src/command/agent/files.rs:103

/// Rewrite `block` to use CRLF line endings when `existing` already does, so a
/// replaced or appended block does not introduce mixed line endings.
fn match_line_endings(existing: &str, block: &str) -> String {
    if existing.contains("\r\n") {
        block.replace("\r\n", "\n").replace('\n', "\r\n")
    } else {
        block.to_string()
    }
}

pub(super) fn upsert_managed_block(existing: &str, block: &str) -> Result<String> {
    let start = find_line_anchored(existing, MANAGED_BLOCK_START, 0);
    let end = find_line_anchored(existing, MANAGED_BLOCK_END, 0);

    match (start, end) {
        // No managed block yet: append a fresh one.
        (None, None) => return Ok(append_managed_block(existing, block)),
        (Some(_), None) => anyhow::bail!(
            "Found only the GitButler managed block start marker. Refusing to edit a partial managed block."
        ),
        (None, Some(_)) => anyhow::bail!(
            "Found only the GitButler managed block end marker. Refusing to edit a partial managed block."
        ),
        (Some(start), Some(end)) if end < start => anyhow::bail!(
            "Found GitButler managed block markers in the wrong order (end before start). Refusing to edit a malformed managed block."
        ),
        // Well-formed: a start marker with an end after it. Fall through to replace.
        (Some(_), Some(_)) => {}
    }

    // Replace the first block with the fresh one and drop any extra blocks
    // (e.g. left over from an earlier buggy run), so the file converges to
    // exactly one block.
    let mut spans = Vec::new();
    for span in managed_block_spans(existing)? {
        let mut block_end = span.end;

View on GitHub (pinned to 58e5313667)