Skip to content

fix: handle Unicode attachment filenames - #7

Open
KateNedelina wants to merge 3 commits into
cdesktop-ai:mainfrom
KateNedelina:fix/unicode-attachment-filenames
Open

fix: handle Unicode attachment filenames#7
KateNedelina wants to merge 3 commits into
cdesktop-ai:mainfrom
KateNedelina:fix/unicode-attachment-filenames

Conversation

@KateNedelina

@KateNedelina KateNedelina commented Aug 13, 2026

Copy link
Copy Markdown

Summary

  • Make attachment filename sanitization safe for UTF-8 names, including Cyrillic filenames.
  • Add regression coverage for Unicode names, Unicode-safe truncation, and the fallback filename.
  • Preserve UTF-8 characters when agent stdout/stderr is split across arbitrary byte-stream chunks.
  • Preserve a useful upload error when the server or proxy returns an empty response body.

Root cause

sanitize_filename measured the sanitized filename with String::len() and then sliced it with a byte offset (clean[..50]). Rust strings are UTF-8, so a filename containing a multi-byte character could be sliced in the middle of that character. The resulting panic aborted the upload request and the browser surfaced only:

Failed to upload attachment:

This was reproduced with a filename containing Cyrillic text (4636__исполнитель_подтвердился...).

The same class of corruption was also present in streamed agent output. The backend decoded each
ReaderStream chunk independently with from_utf8_lossy. When a multi-byte Cyrillic character
was split between two chunks, both incomplete sequences were replaced with , producing text
such as де��ствий in persisted assistant summaries.

Implementation

  • Truncate sanitized stems by Unicode scalar values with .chars().take(...) instead of byte offsets.
  • Keep the existing 50-character sanitized-stem limit and fallback behavior unchanged.
  • Add focused unit tests in crates/services/src/services/file.rs.
  • Add a stateful Utf8ChunkDecoder shared by local process streaming and Codex command-output normalization.
  • Use the same decoder for ACP JSON sent to agent stdin, so user prompts and tool messages remain intact.
  • Keep incomplete UTF-8 sequences between chunks and only emit replacement characters for genuinely invalid bytes.
  • Add tests covering every byte split of a Russian/emoji string and invalid-byte handling.
  • Centralize attachment upload error extraction in the web API client so JSON errors, plain-text errors, HTTP status text, and empty bodies are handled consistently for all three upload endpoints.

Validation

Passing locally:

  • pnpm run format
  • cargo test -p services filename (3 passed)
  • cargo test -p utils utf8_chunk_decoder (2 passed)
  • cargo check -p local-deployment
  • cargo check -p executors
  • pnpm run web-core:check
  • pnpm --filter @vibe/local-web run lint
  • pnpm --filter @vibe/ui run lint
  • Prettier check for the changed web-core file
  • git diff --cached --check

The local checkout cannot reproduce the full GitHub Actions environment: the host does not have the glib-2.0 development package required by the workspace/Tauri crates, and the local Node heap limit is insufficient for the full Vite production bundle at the final chunk-rendering stage. The latest upstream main Test workflow was already failing before this branch (including frontend and backend jobs); the changed packages themselves pass the focused checks above.

Review notes

The commit is intentionally limited to the attachment failure and does not include the unrelated pre-existing local changes in package.json or packages/local-web/vite.config.ts.

@KateNedelina

KateNedelina commented Aug 13, 2026

Copy link
Copy Markdown
Author

CI note: GitHub has held the Test workflow for this fork-based PR in action_required. Approving a workflow run for a fork requires upstream repository administrator rights, so an upstream maintainer must approve the run before its checks can execute. The latest main Test workflow was already failing before this branch; I documented the locally reproducible baseline limitations in the PR description. No merge should occur without the required independent maintainer review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant