gitbutlerapp/gitbutler · error
PR template exists but must be valid UTF-8 text or markdown
Error message
PR template exists but must be valid UTF-8 text or markdown
What it means
review_template_content() converts a fetched PR template FileInfo into a String. A file that exists (size is Some) whose bytes are not valid UTF-8 is rejected rather than lossily decoded, because template content is expected to be UTF-8 text or Markdown.
Source
Thrown at crates/but-api/src/legacy/forge.rs:81
let forge_repo_info = but_forge::derive_forge_repo_info(&base_remote_url)
.context("No forge could be determined for this repository branch")?;
let forge_push_repo_info = if base_remote_url != push_remote_url {
Some(
but_forge::derive_forge_repo_info(&push_remote_url)
.context("Failed to derive forge information for the push repository")?,
)
} else {
None
};
Ok((forge_repo_info, forge_push_repo_info))
}
fn review_template_content(file: FileInfo) -> Result<String> {
if file.size.is_none() {
return Ok(String::new());
}
if !file.is_valid_utf8() {
anyhow::bail!("PR template exists but must be valid UTF-8 text or markdown");
}
Ok(file.content.unwrap_or_default())
}
/// (Deprecated) Get the list of PR template paths for the given project and forge.
/// This function is deprecated in favor of `list_available_review_templates`.
#[but_api]
#[instrument(err(Debug))]
pub fn pr_templates(ctx: &but_ctx::Context, forge: ForgeName) -> Result<Vec<String>> {
Ok(available_review_templates(&ctx.workdir_or_fail()?, &forge))
}
/// Get the forge provider name.
///
/// This is determined by the forge the base branch is pointing to.
/// Returns no value when the project has no target yet or its target forge is unknown.
#[but_api(napi)]
#[instrument(err(Debug))]View on GitHub (pinned to 2497b8007a)
Solutions
- Re-save the template file as UTF-8 in the repository and push the fix
- Remove or rename the offending file so no template is detected
- Callers: catch the error and fall back to an empty template so review creation still works
Example fix
// caller-side graceful degradation
let template = match review_template_content(file) {
Ok(text) => text,
Err(err) if err.to_string().contains("must be valid UTF-8") => {
warn!(?err, "ignoring non-UTF-8 PR template");
String::new()
}
Err(err) => return Err(err),
}; Defensive patterns
Strategy: fallback
Validate before calling
if file.size.is_some() && !file.is_valid_utf8() {
// skip the template (or warn) instead of letting template content fail
} Type guard
fn template_is_usable(file: &FileInfo) -> bool {
file.size.is_none() || file.is_valid_utf8()
} Try / catch
match review_template_content(file) {
Ok(content) => content,
Err(err) if err.to_string().contains("must be valid UTF-8") => String::new(), // degrade gracefully
Err(err) => return Err(err),
} Prevention
- Keep PR templates UTF-8 encoded (lint encoding in CI)
- Treat template loading as best-effort: never block review creation on it
- Strip BOM or binary content when authoring templates
When it happens
Trigger: The repository's pull request template (e.g. .github/PULL_REQUEST_TEMPLATE.md) is encoded as Latin-1/Windows-1252 or contains stray binary bytes, and a review/PR flow requests its content from the forge.
Common situations: Templates authored in Windows editors saving ANSI encoding; non-UTF-8 smart quotes or icons pasted in; a binary file accidentally named like a template.
Related errors
- Branch `{}` is pushed, but its remote ancestry does not matc
- Could not restore all review targets after the push failed:
- No Bitbucket access token found for account '{account_id}'.\
- Failed to create pull request: {status} - {error_text}
AI-assisted analysis of gitbutlerapp/gitbutler@2497b8007a (2026-08-17).
Data as JSON: /api/errors/e945aa82f7cb4315.
Report an issue: GitHub.