gitbutlerapp/gitbutler · error

Found GitButler managed block markers in the wrong order…

Error message

Found GitButler managed block markers in the wrong order (end before start). Refusing to edit a malformed managed block.

What it means

Both managed block markers exist but the end marker appears before the start marker, so the block is malformed. The tool bails out instead of replacing what it cannot safely interpret as a contiguous block.

Solutions

  1. Open the file and reorder markers so the start marker precedes the end marker.
  2. Remove all markers and let the upsert append one fresh, well-ordered block.
  3. Verify only one start/end pair exists, then re-run the upsert.

Example fix

// before
<!-- GITBUTLER-MANAGED-BLOCK:END -->
...content...
<!-- GITBUTLER-MANAGED-BLOCK:START -->
// after
<!-- GITBUTLER-MANAGED-BLOCK:START -->
...content...
<!-- GITBUTLER-MANAGED-BLOCK:END -->
Defensive patterns

Strategy: validation

Validate before calling

const content = fs.readFileSync(file, 'utf8');
const startIdx = content.indexOf('GITBUTLER-MANAGED-BLOCK:START');
const endIdx = content.indexOf('GITBUTLER-MANAGED-BLOCK:END');
if (startIdx !== -1 && endIdx !== -1 && endIdx < startIdx) throw new Error(`Managed block markers out of order in ${file}`);

Prevention

When it happens

Trigger: upsert_managed_block finds MANAGED_BLOCK_END at a line index earlier than MANAGED_BLOCK_START — e.g. duplicated blocks after manual editing, reordered lines, or a botched merge that swapped marker order.

Common situations: Users rearranging their instructions file; copy-pasting blocks out of order; merge conflicts resolved by keeping marker lines in the wrong sequence.

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/c082538ecce1b65e. Report an issue: GitHub.

Appendix: source

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

    } 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;
        if existing[block_end..].starts_with("\r\n") {
            block_end += 2;
        } else if existing[block_end..].starts_with('\n') {
            block_end += 1;
        }
        spans.push((span.start, block_end));

View on GitHub (pinned to 58e5313667)