{"record":{"id":"e945aa82f7cb4315","repo":"gitbutlerapp/gitbutler","slug":"pr-template-exists-but-must-be-valid-utf-8-text-or","errorCode":null,"errorMessage":"PR template exists but must be valid UTF-8 text or markdown","messagePattern":"PR template exists but must be valid UTF-8 text or markdown","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-api/src/legacy/forge.rs","lineNumber":81,"sourceCode":"    let forge_repo_info = but_forge::derive_forge_repo_info(&base_remote_url)\n        .context(\"No forge could be determined for this repository branch\")?;\n    let forge_push_repo_info = if base_remote_url != push_remote_url {\n        Some(\n            but_forge::derive_forge_repo_info(&push_remote_url)\n                .context(\"Failed to derive forge information for the push repository\")?,\n        )\n    } else {\n        None\n    };\n    Ok((forge_repo_info, forge_push_repo_info))\n}\n\nfn review_template_content(file: FileInfo) -> Result<String> {\n    if file.size.is_none() {\n        return Ok(String::new());\n    }\n    if !file.is_valid_utf8() {\n        anyhow::bail!(\"PR template exists but must be valid UTF-8 text or markdown\");\n    }\n    Ok(file.content.unwrap_or_default())\n}\n\n/// (Deprecated) Get the list of PR template paths for the given project and forge.\n/// This function is deprecated in favor of `list_available_review_templates`.\n#[but_api]\n#[instrument(err(Debug))]\npub fn pr_templates(ctx: &but_ctx::Context, forge: ForgeName) -> Result<Vec<String>> {\n    Ok(available_review_templates(&ctx.workdir_or_fail()?, &forge))\n}\n\n/// Get the forge provider name.\n///\n/// This is determined by the forge the base branch is pointing to.\n/// Returns no value when the project has no target yet or its target forge is unknown.\n#[but_api(napi)]\n#[instrument(err(Debug))]","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/2497b8007aa4a1922dae9a805b32ffe5b5037785/crates/but-api/src/legacy/forge.rs#L63-L99","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// caller-side graceful degradation\nlet template = match review_template_content(file) {\n    Ok(text) => text,\n    Err(err) if err.to_string().contains(\"must be valid UTF-8\") => {\n        warn!(?err, \"ignoring non-UTF-8 PR template\");\n        String::new()\n    }\n    Err(err) => return Err(err),\n};","handlingStrategy":"fallback","validationCode":"if file.size.is_some() && !file.is_valid_utf8() {\n    // skip the template (or warn) instead of letting template content fail\n}","typeGuard":"fn template_is_usable(file: &FileInfo) -> bool {\n    file.size.is_none() || file.is_valid_utf8()\n}","tryCatchPattern":"match review_template_content(file) {\n    Ok(content) => content,\n    Err(err) if err.to_string().contains(\"must be valid UTF-8\") => String::new(), // degrade gracefully\n    Err(err) => return Err(err),\n}","preventionTips":["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"],"tags":["forge","pull-request","template","utf-8","encoding"],"backgroundTag":"invalid-utf-8-content","analyzedSha":"2497b8007aa4a1922dae9a805b32ffe5b5037785","analyzedAt":"2026-08-17T00:30:25.648Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}