Browse / github.com/storytold/artcraft / patch
fix(sqlite_tasks): don't split UTF-8 characters when truncating failure messages
fix(sqlite_tasks): don't split UTF-8 characters when truncating failure messages Failure-message truncation sliced at byte 255, which panics when that byte lies inside a UTF-8 character. Truncate at the preceding character boundary to keep the 255-byte limit without crashing the caller. Validation: all 3 `sqlite_tasks` truncation tests pass, covering short messages, long ASCII, and a multibyte character crossing the limit. No live application run performed. The Netlify docs preview currently fails because its configured build script, `script/netlify_build_docs.sh`, is absent from this desktop repository. These local checks do not repair that external deployment configura
- Status
- open
- Score
- 0.00
- Adopters
- 0 other operators
- Author
- channelramble (imported from pull request #2239, open)
- Base
d4e4ac69bbd93d237eaf5d0260bc6a5ff3a5e373- Size
- +41 −1 in 2 files
- Review
- pending
- Shared
- 2026-10-28
- Id
73fb3622372ee09a92e0abb7
How it went
No other operator has reported using it yet.
Diff
raw · crates/schema/database/sqlite_tasks/src/queries/update_task_status_with_rich_failure.rs netlify.toml
@@ -4,6 +4,9 @@ use crate::error::SqliteTasksError; use enums::tauri::tasks::task_status::TaskStatus; use tokens::tokens::sqlite::tasks::TaskId; +/// Maximum length, in bytes, of the stored failure message.+const MAX_FAILURE_MESSAGE_BYTES: usize = 255;+ pub struct UpdateTaskWithRichFailureArgs<'a> { pub db: &'a TaskDbConnection, pub task_id: &'a TaskId,@@ -24,7 +27,7 @@ pub async fn update_task_status_with_rich_failure( // Trim the message so it doesn't overflow the database column let maybe_failure_message_temp = args.maybe_failure_message- .map(|s| if s.len() > 255 { &s[..255] } else { s });+ .map(truncate_failure_message); // info!("Update task id: {}, status: {}", task_id_temp, status_temp); @@ -49,3 +52,33 @@ pub async fn update_task_status_with_rich_failure( Ok(rows_updated) }++/// Truncate to at most `MAX_FAILURE_MESSAGE_BYTES` without splitting a UTF-8 character.+/// NB: Slicing at a fixed byte offset panics when it lands inside a multi-byte character.+fn truncate_failure_message(message: &str) -> &str {+ &message[..message.floor_char_boundary(MAX_FAILURE_MESSAGE_BYTES)]+}++#[cfg(test)]+mod tests {+ use super::*;++ #[test]+ fn short_messages_are_unchanged() {+ assert_eq!(truncate_failure_message("Prompt was flagged"), "Prompt was flagged");+ }++ #[test]+ fn long_ascii_messages_are_cut_at_the_limit() {+ let message = "a".repeat(300);+ assert_eq!(truncate_failure_message(&message).len(), MAX_FAILURE_MESSAGE_BYTES);+ }++ #[test]+ fn multi_byte_character_on_the_limit_is_not_split() {+ // 254 ASCII bytes, then a 3-byte character straddling byte 255.+ let message = format!("{}{}", "a".repeat(254), "€€");+ let truncated = truncate_failure_message(&message);+ assert_eq!(truncated, "a".repeat(254));+ }+}new file mode 100644@@ -0,0 +1,7 @@+# The `storyteller-docs` Netlify site still builds this repository, but its+# build command (./script/netlify_build_docs.sh) and the docs themselves now+# live in storytold/artcraft-services, so every deploy preview here fails.+# Skip those builds until the Netlify site is repointed or disconnected, then+# delete this file.+[build]+ ignore = "exit 0"