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
- Open the file and restore the missing GitButler managed block end marker after the start marker.
- Alternatively delete the start marker entirely so upsert_managed_block appends a fresh, complete block.
- 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
- Never hand-edit the lines between managed block markers; only edit outside the block.
- After merging or conflict resolution, diff the file and verify both markers survive in order.
- Teach linters/formatters to ignore the managed block region.
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
- Found only the GitButler managed block end marker. Refusing…
- Found GitButler managed block markers in the wrong order…
- Ambiguous branch ' ', matches multiple items
- Ambiguous branch ' ', matches multiple items
- `but land` requires an active GitButler workspace…
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)