Skip to content

fix(ipc): handle version mismatches gracefully without confusing bincode errors#213

Closed
paperbenni wants to merge 3 commits intomainfrom
fix/bincode-version-mismatch-8945557543414012940
Closed

fix(ipc): handle version mismatches gracefully without confusing bincode errors#213
paperbenni wants to merge 3 commits intomainfrom
fix/bincode-version-mismatch-8945557543414012940

Conversation

@paperbenni
Copy link
Copy Markdown
Member

@paperbenni paperbenni commented Mar 12, 2026

This PR fixes an issue where the instantwmctl tool would throw a confusing UnexpectedVariant error when communicating with an older/newer instantwm instance due to a bincode struct layout mismatch.

Changes:

  • build.rs: Modified the IPC protocol version generation to include the git commit hash. This makes versioning more robust between builds.
  • src/ipc.rs: The IPC server now safely decodes only the version string from the buffer first. If it detects a version mismatch, it bails out early with a clear error message instead of trying to parse the mismatched struct variants and failing cryptically.
  • src/bin/instantwmctl.rs: Updated the generic deserialize error message to provide a helpful hint that it is likely a version mismatch.

PR created automatically by Jules for task 8945557543414012940 started by @paperbenni

Summary by Sourcery

Improve IPC protocol compatibility handling between instantwmctl clients and instantWM servers to surface clear version mismatch errors instead of low-level deserialization failures.

Bug Fixes:

  • Detect IPC protocol version mismatches early by decoding the version string first and returning a clear error response instead of bincode variant errors.

Enhancements:

  • Make the IPC protocol version string more robust by incorporating the git commit hash into the generated version.
  • Clarify instantwmctl deserialization failure output with guidance that mismatches between instantwmctl and instantWM versions are the likely cause and how to resolve them.

Summary by CodeRabbit

  • New Features

    • Added urgent tag and window color scheme support with configurable appearance.
    • Enhanced tag interaction with improved dragging support.
  • Bug Fixes

    • Improved JSON parsing robustness with trailing comma handling.
    • Enhanced error messaging for version compatibility issues.
  • Refactor

    • Restructured IPC system into modular command handlers.
    • Refined cursor resize direction mappings for improved accuracy.
    • Optimized gesture tracking for bar interactions.

When the `instantwmctl` client and the `instantwm` server were built from different commits (resulting in different struct/enum layouts), `bincode` would fail during deserialization with a generic `UnexpectedVariant` error. This commit implements two fixes:

1. **Protocol Versioning**: Appends the first 8 characters of the current git commit hash to the `IPC_PROTOCOL_VERSION` generated in `build.rs`. This ensures different builds uniquely identify themselves.
2. **Graceful Deserialization**: In the server IPC handler, we now partially decode just the version `String` from the byte buffer *before* attempting to deserialize the entire `IpcRequest` struct. If a mismatch is detected, we can immediately return a friendly error message instead of letting bincode crash.
3. **Friendly Errors**: The client-side (`instantwmctl.rs`) now provides a clearer error message indicating a version mismatch when deserialization fails.

Co-authored-by: paperbenni <15818888+paperbenni@users.noreply.github.com>
@google-labs-jules
Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Mar 12, 2026

Reviewer's Guide

Improves IPC protocol compatibility handling by strengthening version identification and adding graceful error handling and messaging when client/server versions of instantwm and instantwmctl do not match.

Sequence diagram for IPC request handling with graceful version mismatch

sequenceDiagram
    actor User
    participant Instantwmctl as instantwmctl
    participant IpcServer as IpcServer

    User->>Instantwmctl: Run command
    Instantwmctl->>IpcServer: Send IPC request bytes

    Note over IpcServer: New behavior: decode version string first
    IpcServer->>IpcServer: bincode_decode_from_slice_String(buffer)
    alt Version_decoded_and_mismatch
        IpcServer->>Instantwmctl: IpcResponse_err("version mismatch: client is X, server is Y. Please ensure instantwmctl and instantWM are the same version.")
        Instantwmctl->>User: Print error message
    else Version_decode_fails
        IpcServer->>IpcServer: Treat as mismatch_or_garbage
        IpcServer->>IpcServer: Continue to full request decode
        IpcServer->>IpcServer: bincode_decode_from_slice_IpcRequest(buffer)
        alt Full_decode_ok_and_version_valid
            IpcServer->>IpcServer: request_validate_version()
            IpcServer-->>Instantwmctl: Normal response
        else Full_decode_error
            IpcServer-->>Instantwmctl: IpcResponse_err("deserialize error (likely version mismatch): e")
        end
    else Version_matches
        IpcServer->>IpcServer: bincode_decode_from_slice_IpcRequest(buffer)
        alt Full_decode_ok_and_version_valid
            IpcServer->>IpcServer: request_validate_version()
            IpcServer-->>Instantwmctl: Normal response
        else Full_decode_error
            IpcServer-->>Instantwmctl: IpcResponse_err("deserialize error (likely version mismatch): e")
        end
    end

    Instantwmctl->>Instantwmctl: Deserialize response
    alt Deserialize_success
        Instantwmctl->>User: Show command result
    else Deserialize_failure
        Instantwmctl->>User: Print "instantwmctl: deserialization failed: e"
        Instantwmctl->>User: Print "This is likely caused by a version mismatch between instantwmctl and instantWM."
        Instantwmctl->>User: Print "Please ensure they are built from the same commit."
        Instantwmctl->>Instantwmctl: Exit 1
    end
Loading

Flow diagram for IPC protocol version generation in build.rs

flowchart TD
    A[Start build_rs main] --> B[Read CARGO_PKG_VERSION as crate_version]
    B --> C[compute_ipc_source_hash to get source_hash]
    C --> D[git_head_commit to get build_commit or fallback to unknown]
    D --> E[Take first 8 chars of source_hash]
    E --> F[Take first 8 chars of build_commit]
    F --> G[Format protocol_version as crate_version-source_hash8-build_commit8]
    G --> H[Print cargo:rustc-env=IPC_PROTOCOL_VERSION=protocol_version]
    D --> I[Print cargo:rustc-env=INSTANTWM_BUILD_COMMIT=build_commit]
    H --> J[End]
    I --> J[End]
Loading

File-Level Changes

Change Details Files
Add a preliminary IPC version-string decode step to detect and handle protocol mismatches before decoding full requests.
  • Decode only a String from the incoming IPC buffer using bincode prior to full request deserialization.
  • Compare the decoded client version to IPC_PROTOCOL_VERSION and, on mismatch, send a clear error response describing client and server versions and advising to align versions.
  • Skip full request parsing when the version check fails.
  • On generic deserialization failures, update the error message to state it is likely a version mismatch while preserving the original error text.
  • Retain validate_version on the decoded request as a secondary safety check.
src/ipc.rs
Strengthen IPC protocol version uniqueness using both source hash and git commit hash in build-time generation.
  • Retrieve the current git HEAD commit hash during build, defaulting to "unknown" when unavailable.
  • Extend IPC_PROTOCOL_VERSION format to include crate version, truncated IPC source hash, and truncated git commit hash.
  • Continue exporting INSTANTWM_BUILD_COMMIT as a separate environment variable for runtime introspection.
build.rs
Improve CLI feedback in instantwmctl when deserialization of IPC responses fails.
  • After printing the bincode deserialization error, add explanatory lines indicating it is likely caused by an instantwmctl/instantWM version mismatch.
  • Advise the user to ensure both components are built from the same commit before exiting with a non-zero status.
src/bin/instantwmctl.rs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 12, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c1fd14b8-6bdc-4327-9b7a-57a0a3fd8b5a

📥 Commits

Reviewing files that changed from the base of the PR and between cc97e7d and 18f6c19.

📒 Files selected for processing (24)
  • build.rs
  • src/animation.rs
  • src/bar/status.rs
  • src/bar/widgets.rs
  • src/bin/instantwmctl.rs
  • src/config/appearance.rs
  • src/config/buttons.rs
  • src/globals.rs
  • src/ipc.rs
  • src/ipc/general.rs
  • src/ipc/input.rs
  • src/ipc/keyboard.rs
  • src/ipc/mod.rs
  • src/ipc/mode.rs
  • src/ipc/monitor.rs
  • src/ipc/scratchpad.rs
  • src/ipc/tag.rs
  • src/ipc/toggle.rs
  • src/ipc/window.rs
  • src/keyboard.rs
  • src/mouse/cursor.rs
  • src/startup/wayland/bar.rs
  • src/startup/wayland/input.rs
  • src/types/color.rs

📝 Walkthrough

Walkthrough

This PR introduces urgent window/tag state support with dedicated color schemes, restructures the IPC module from a monolithic file into domain-specific submodules (general, input, keyboard, mode, monitor, scratchpad, tag, toggle, window), adds Git commit hash tracking to the protocol version, and implements various logic adjustments across animation, cursor handling, and keyboard state management.

Changes

Cohort / File(s) Summary
Urgent State Support
src/types/color.rs, src/config/appearance.rs, src/globals.rs, src/bar/widgets.rs
Added Urgent variants to SchemeTag and SchemeWin enums; extended TagColorSet and WindowColorSet with urgent color fields; updated tag_scheme to prioritize urgent tags; modified widget calls to pass urgency information.
IPC Module Restructuring
src/ipc.rs (deleted), src/ipc/mod.rs, src/ipc/general.rs, src/ipc/input.rs, src/ipc/keyboard.rs, src/ipc/mode.rs, src/ipc/monitor.rs, src/ipc/scratchpad.rs, src/ipc/tag.rs, src/ipc/toggle.rs, src/ipc/window.rs
Refactored monolithic IPC server into modular structure with domain-specific handlers; moved server logic to mod.rs and distributed command handlers across submodules for wallpaper, actions, spawning, focus, layout, status, input config, keyboard, mode, monitor, scratchpad, tag, toggle, and window management.
Build and Protocol
build.rs
Added retrieval of Git HEAD commit hash and included first 8 characters in protocol version string for version tracking.
UI and Input Handling
src/startup/wayland/bar.rs, src/startup/wayland/input.rs, src/mouse/cursor.rs, src/keyboard.rs
Fixed gesture state preservation in Wayland bar input; refined tag-drag detection in pointer motion; expanded diagonal cursor mapping from grouped to explicit per-direction handling; inverted tiling layout logic in keyboard overview state.
Animation and Parsing
src/animation.rs, src/bar/status.rs, src/bin/instantwmctl.rs
Removed directional remapping for tiled animation; added trailing comma stripping to permissive JSON parsing; enhanced IPC deserialization error messaging with version mismatch guidance.
Button Configuration
src/config/buttons.rs
Introduced tag_mask_from_pos helper; updated mouse button bindings to derive TagMask from bar position with fallback when position is invalid.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

Possibly related PRs

Poem

🐰 Urgency blooms in color now,
IPC scattered, modular bow,
Commit hash captured in the wind,
Buttons wiser where they pinned—
A rabbit hops through cleaner code!

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/bincode-version-mismatch-8945557543414012940
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@paperbenni
Copy link
Copy Markdown
Member Author

@jules ipc.rs has since been refactored, resolve the merge conflicts

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • The IPC handler now validates the protocol version twice (manual string decode plus request.validate_version()); consider centralizing this check in one place to avoid divergence in future changes.
  • When the initial String decode for the version fails, the code silently proceeds to full deserialization; it might be helpful to log or at least respond with a clearer error in that branch to distinguish truly malformed input from other issues.
  • Including "unknown" as the git commit in the protocol version couples compatibility to a potentially meaningless tag; you may want to omit the commit from IPC_PROTOCOL_VERSION when it cannot be determined, to avoid spurious version mismatches between identical builds without git metadata.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The IPC handler now validates the protocol version twice (manual string decode plus `request.validate_version()`); consider centralizing this check in one place to avoid divergence in future changes.
- When the initial `String` decode for the version fails, the code silently proceeds to full deserialization; it might be helpful to log or at least respond with a clearer error in that branch to distinguish truly malformed input from other issues.
- Including `"unknown"` as the git commit in the protocol version couples compatibility to a potentially meaningless tag; you may want to omit the commit from `IPC_PROTOCOL_VERSION` when it cannot be determined, to avoid spurious version mismatches between identical builds without git metadata.

## Individual Comments

### Comment 1
<location path="src/ipc.rs" line_range="88-97" />
<code_context>
         }

+        // First try to decode just the version string to detect mismatches gracefully
+        match bincode::decode_from_slice::<String, _>(&buffer, bincode::config::standard()) {
+            Ok((version, _)) => {
+                if version != crate::ipc_types::IPC_PROTOCOL_VERSION {
+                    let _ = send_response(
+                        &mut stream,
+                        &IpcResponse::err(format!(
+                            "version mismatch: client is {}, server is {}. Please ensure instantwmctl and instantWM are the same version.",
+                            version, crate::ipc_types::IPC_PROTOCOL_VERSION
+                        )),
+                    );
+                    return;
+                }
+            }
+            Err(_) => {
+                // If we can't even decode a string, it's definitely a mismatch or garbage
+            }
</code_context>
<issue_to_address>
**issue (bug_risk):** The initial version-string decode error is silently ignored and the code continues as if nothing happened.

On `Err(_)` we fall through and still try to decode the full `IpcRequest`, which contradicts the comment and may surface a second, less-specific error to the client. Either return early on `Err(_)` with a clear mismatch/garbage error, or adjust the comment and logic to clearly express an intentional best-effort fallback (e.g. for backward compatibility).
</issue_to_address>

### Comment 2
<location path="src/bin/instantwmctl.rs" line_range="754-755" />
<code_context>
         Ok((r, _)) => r,
         Err(e) => {
             eprintln!("instantwmctl: deserialization failed: {}", e);
+            eprintln!("This is likely caused by a version mismatch between instantwmctl and instantWM.");
+            eprintln!("Please ensure they are built from the same commit.");
             std::process::exit(1);
         }
</code_context>
<issue_to_address>
**suggestion:** The version-mismatch hint could be more actionable by including the detected protocol or build versions.

Since `IPC_PROTOCOL_VERSION` and `INSTANTWM_BUILD_COMMIT` are already embedded via build.rs, consider printing those (and/or the client’s equivalents) with this hint so users can immediately see which versions/commits are mismatched without extra investigation.

Suggested implementation:

```rust
        Ok((r, _)) => r,
        Err(e) => {
            eprintln!("instantwmctl: deserialization failed: {}", e);
            eprintln!("This is likely caused by a version mismatch between instantwmctl and instantWM.");

            eprintln!(
                "instantwmctl: IPC protocol version: {}, build commit: {}",
                IPC_PROTOCOL_VERSION,
                INSTANTWM_BUILD_COMMIT
            );
            eprintln!("Please ensure instantwmctl and instantWM are built from the same commit and use the same IPC protocol version.");
            std::process::exit(1);
        }
    };

```

1. Ensure `IPC_PROTOCOL_VERSION` and `INSTANTWM_BUILD_COMMIT` are in scope in `src/bin/instantwmctl.rs`. If they are defined in the crate root or another module, you may need to add:
   - `use crate::IPC_PROTOCOL_VERSION;`
   - `use crate::INSTANTWM_BUILD_COMMIT;`
   or equivalent module paths at the top of this file.
2. If the constants are exposed under a different namespace (e.g. `instantwm::IPC_PROTOCOL_VERSION`), adjust the references in the new `eprintln!` call accordingly.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/ipc.rs Outdated
Comment on lines +88 to +97
match bincode::decode_from_slice::<String, _>(&buffer, bincode::config::standard()) {
Ok((version, _)) => {
if version != crate::ipc_types::IPC_PROTOCOL_VERSION {
let _ = send_response(
&mut stream,
&IpcResponse::err(format!(
"version mismatch: client is {}, server is {}. Please ensure instantwmctl and instantWM are the same version.",
version, crate::ipc_types::IPC_PROTOCOL_VERSION
)),
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): The initial version-string decode error is silently ignored and the code continues as if nothing happened.

On Err(_) we fall through and still try to decode the full IpcRequest, which contradicts the comment and may surface a second, less-specific error to the client. Either return early on Err(_) with a clear mismatch/garbage error, or adjust the comment and logic to clearly express an intentional best-effort fallback (e.g. for backward compatibility).

Comment thread src/bin/instantwmctl.rs
Comment on lines +754 to +755
eprintln!("This is likely caused by a version mismatch between instantwmctl and instantWM.");
eprintln!("Please ensure they are built from the same commit.");
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: The version-mismatch hint could be more actionable by including the detected protocol or build versions.

Since IPC_PROTOCOL_VERSION and INSTANTWM_BUILD_COMMIT are already embedded via build.rs, consider printing those (and/or the client’s equivalents) with this hint so users can immediately see which versions/commits are mismatched without extra investigation.

Suggested implementation:

        Ok((r, _)) => r,
        Err(e) => {
            eprintln!("instantwmctl: deserialization failed: {}", e);
            eprintln!("This is likely caused by a version mismatch between instantwmctl and instantWM.");

            eprintln!(
                "instantwmctl: IPC protocol version: {}, build commit: {}",
                IPC_PROTOCOL_VERSION,
                INSTANTWM_BUILD_COMMIT
            );
            eprintln!("Please ensure instantwmctl and instantWM are built from the same commit and use the same IPC protocol version.");
            std::process::exit(1);
        }
    };
  1. Ensure IPC_PROTOCOL_VERSION and INSTANTWM_BUILD_COMMIT are in scope in src/bin/instantwmctl.rs. If they are defined in the crate root or another module, you may need to add:
    • use crate::IPC_PROTOCOL_VERSION;
    • use crate::INSTANTWM_BUILD_COMMIT;
      or equivalent module paths at the top of this file.
  2. If the constants are exposed under a different namespace (e.g. instantwm::IPC_PROTOCOL_VERSION), adjust the references in the new eprintln! call accordingly.

@google-labs-jules
Copy link
Copy Markdown
Contributor

@jules ipc.rs has since been refactored, resolve the merge conflicts

I've resolved the merge conflict by applying the bincode deserialization fix directly to src/ipc/mod.rs which was introduced in the recent refactor.

When the `instantwmctl` client and the `instantwm` server were built from different commits (resulting in different struct/enum layouts), `bincode` would fail during deserialization with a generic `UnexpectedVariant` error. This commit implements two fixes:

1. **Protocol Versioning**: Appends the first 8 characters of the current git commit hash to the `IPC_PROTOCOL_VERSION` generated in `build.rs`. This ensures different builds uniquely identify themselves.
2. **Graceful Deserialization**: In the server IPC handler (`src/ipc/mod.rs`), we now partially decode just the version `String` from the byte buffer *before* attempting to deserialize the entire `IpcRequest` struct. If a mismatch is detected, we can immediately return a friendly error message instead of letting bincode crash.
3. **Friendly Errors**: The client-side (`instantwmctl.rs`) now provides a clearer error message indicating a version mismatch when deserialization fails.

Co-authored-by: paperbenni <15818888+paperbenni@users.noreply.github.com>
@paperbenni
Copy link
Copy Markdown
Member Author

@jules
merge conflict, solve, merge stuff

When the `instantwmctl` client and the `instantwm` server were built from different commits (resulting in different struct/enum layouts), `bincode` would fail during deserialization with a generic `UnexpectedVariant` error. This commit implements two fixes:

1. **Protocol Versioning**: Appends the first 8 characters of the current git commit hash to the `IPC_PROTOCOL_VERSION` generated in `build.rs`. This ensures different builds uniquely identify themselves.
2. **Graceful Deserialization**: In the server IPC handler (`src/ipc/mod.rs`), we now partially decode just the version `String` from the byte buffer *before* attempting to deserialize the entire `IpcRequest` struct. If a mismatch is detected, we can immediately return a friendly error message instead of letting bincode crash.
3. **Friendly Errors**: The client-side (`instantwmctl.rs`) now provides a clearer error message indicating a version mismatch when deserialization fails.

Co-authored-by: paperbenni <15818888+paperbenni@users.noreply.github.com>
@paperbenni
Copy link
Copy Markdown
Member Author

gggg

@paperbenni paperbenni closed this Mar 12, 2026
@google-labs-jules
Copy link
Copy Markdown
Contributor

gggg

Acknowledged.

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