BloopAI/vibe-kanban · error

Workspace serialization should not fail

Error message

Workspace serialization should not fail

What it means

This panic comes from `.expect()` on `serde_json::to_value(workspace)` in `workspace_patch::add` (crates/services/src/services/events/patches.rs:72). The library assumes `WorkspaceWithStatus` always serializes to JSON; serde returns `Err` only when a custom `Serialize` implementation errors (e.g. serializing a map with non-string keys or a manual impl returning Err). This is treated as an internal invariant, not an expected runtime failure.

Source

Thrown at crates/services/src/services/events/patches.rs:72

/// Helper functions for creating workspace-specific patches
pub mod workspace_patch {
    use super::*;

    fn workspace_path(workspace_id: Uuid) -> String {
        format!(
            "/workspaces/{}",
            escape_pointer_segment(&workspace_id.to_string())
        )
    }

    pub fn add(workspace: &WorkspaceWithStatus) -> Patch {
        Patch(vec![PatchOperation::Add(AddOperation {
            path: workspace_path(workspace.id)
                .try_into()
                .expect("Workspace path should be valid"),
            value: serde_json::to_value(workspace)
                .expect("Workspace serialization should not fail"),
        })])
    }

    pub fn replace(workspace: &WorkspaceWithStatus) -> Patch {
        Patch(vec![PatchOperation::Replace(ReplaceOperation {
            path: workspace_path(workspace.id)
                .try_into()
                .expect("Workspace path should be valid"),
            value: serde_json::to_value(workspace)
                .expect("Workspace serialization should not fail"),
        })])
    }

    pub fn remove(workspace_id: Uuid) -> Patch {
        Patch(vec![PatchOperation::Remove(RemoveOperation {
            path: workspace_path(workspace_id)
                .try_into()
                .expect("Workspace path should be valid"),

View on GitHub (pinned to 4deb7eca8f)

Solutions

  1. Inspect `WorkspaceWithStatus` (and nested types) for custom `Serialize` impls that can return Err; fix the impl so it cannot fail.
  2. If a field serializes a map with non-string keys, convert keys to strings first (e.g. BTreeMap<String, T>) before serializing.
  3. If fallibility is real, replace `.expect(...)` with error propagation (return Result<Patch, serde_json::Error>) and handle at the event-emission layer.
  4. Regenerate/verify derived types with `pnpm run generate-types` after changing the struct.

Example fix

// before
value: serde_json::to_value(workspace)
    .expect("Workspace serialization should not fail"),
// after
value: serde_json::to_value(workspace).unwrap_or_else(|e| {
    panic!("failed to serialize workspace {}: {e}", workspace.id)
}),
// or propagate: return Result<Patch, serde_json::Error>
Defensive patterns

Strategy: validation

Validate before calling

let value = serde_json::to_value(&workspace)
    .map_err(|e| anyhow::anyhow!("workspace serialization failed: {e}"))?;
// proceed with Patch construction using `value`

Type guard

fn serializable<T: serde::Serialize>(v: &T) -> Option<serde_json::Value> {
    serde_json::to_value(v).ok()
}

Try / catch

match serde_json::to_value(workspace) {
    Ok(v) => /* build patch with v */,
    Err(e) => log::error!("workspace {} serialization failed: {e}", workspace.id),
}

Prevention

When it happens

Trigger: Calling `workspace_patch::add(&WorkspaceWithStatus)` when `serde_json::to_value` fails — only possible if a type inside `WorkspaceWithStatus` has a custom/fallible `Serialize` impl (e.g. serializing a HashMap with non-string keys, or a manual impl returning Err).

Common situations: Developers encounter this after adding a field with a hand-written `Serialize` implementation that can fail, or after swapping a struct field to a map keyed by UUIDs/other non-string types.

Related errors


AI-assisted analysis of BloopAI/vibe-kanban@4deb7eca8f (2026-08-29). Data as JSON: /api/errors/803c87272f8ee311. Report an issue: GitHub.