Skip to content

Return the deserialized object when CIM classes cannot be rehydrated - #28192

Open
Nova-Logic wants to merge 2 commits into
PowerShell:masterfrom
Nova-Logic:fix-cim-rehydration-without-native-mi
Open

Nova-Logic wants to merge 2 commits into
PowerShell:masterfrom
Nova-Logic:fix-cim-rehydration-without-native-mi

Conversation

@Nova-Logic

Copy link
Copy Markdown

PR Summary

Deserializing a CIM instance (CliXml with __ClassMetadata) rehydrates its CIM class through the native MI library. On platforms where that library is not available - macOS ships no libmi - creating CimDeserializer throws DllNotFoundException. InternalDeserializer.RehydrateCimClass only catches CimException, so the exception escapes and the whole deserialization fails, although RehydrateCimInstance is designed to fall back to the deserialized property bag when a class cannot be rehydrated.

This PR treats an unavailable CIM deserializer like any other rehydration failure: RehydrateCimClass returns null, RehydrateCimInstance returns the deserialized object (Deserialized.Microsoft.Management.Infrastructure.CimInstance#...) with all its properties, and the failure is remembered so the native load is not retried for every object.

Repro on macOS (7.5.4 release and current master):

# any CIM-based cmdlet on a Windows host over SSH remoting
Invoke-Command -HostName winhost -UserName user { Get-CimInstance Win32_TimeZone }
# OperationStopped: Unable to load shared library 'libmi' or one of its dependencies ...

# or offline, with CliXml produced on Windows by [PSSerializer]::Serialize((Get-CimInstance Win32_TimeZone), 1)
[System.Management.Automation.PSSerializer]::Deserialize($xml)
# MethodInvocationException: ... Unable to load shared library 'libmi' ...

With this change both return the object with its properties (e.g. Bias, Caption), typed as Deserialized.Microsoft.Management.Infrastructure.CimInstance#root/cimv2/Win32_TimeZone.

PR Context

Managing Windows servers from macOS over SSH remoting is common, and many Windows cmdlets return CIM instances (Get-StorageQoSFlow, Get-Disk, Get-Volume, Get-NetAdapter, ...). Today none of them can be used from a macOS client: the call fails instead of returning deserialized objects, which is what PowerShell already returns whenever CIM rehydration is not possible for any other reason.

Behaviour where the MI library is available (Windows, Linux) is unchanged: CIM instances are still rehydrated.

PR Checklist

Test added to test/powershell/engine/Api/Serialization.Tests.ps1 with the asset assets/CimInstance-Win32_TimeZone.clixml (a Win32_TimeZone instance serialized on Windows with class metadata). It asserts the deserialized object's properties and type name, so it passes both where the instance is rehydrated (Windows, Linux) and where it is not (macOS). On macOS it fails with 7.5.4 ("Unable to load shared library 'libmi'") and passes with this change. Also verified manually from macOS: Invoke-Command to a Windows Server 2022 host running Get-StorageQoSFlow now returns the flows instead of failing.

@Nova-Logic
Nova-Logic requested a review from a team as a code owner October 12, 2026 22:51
Copilot AI balanced review requested due to automatic review settings October 12, 2026 22:51
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

The fallback is narrowly scoped, thread-safe, and covered by an appropriate cross-platform regression test.

0 open findings

What changed in this PR

Prevents CIM deserialization from failing when the native MI library is unavailable, preserving deserialized property bags instead.

Changes:

  • Gracefully handles native CIM deserializer initialization failures.
  • Caches deserializer unavailability to avoid repeated attempts.
  • Adds cross-platform regression coverage and CIM CliXml fixture.
File Description
src/​System.Management.Automation/​engine/​serialization.cs Adds fallback handling for unavailable CIM rehydration.
test/​powershell/​engine/​Api/​Serialization.Tests.ps1 Verifies CIM data remains accessible after deserialization.
test/​powershell/​engine/​Api/​assets/​CimInstance-Win32_TimeZone.clixml Provides serialized CIM class metadata for testing.

🧠 Review effort: Balanced


💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Deserializing a CIM instance rehydrates its CIM class through the native
MI library. Where that library is not available (for example macOS, which
ships no libmi), loading CimDeserializer throws DllNotFoundException.
RehydrateCimClass only caught CimException, so the whole deserialization
failed - e.g. Invoke-Command against a Windows host running any CIM-based
cmdlet (Get-StorageQoSFlow, Get-Disk, ...) failed from macOS.

Treat an unavailable CIM deserializer like any other rehydration failure:
keep the deserialized property bag, as RehydrateCimInstance already does
when the class cannot be rehydrated, and remember the failure so the
native load is not retried for every object.
@Nova-Logic
Nova-Logic force-pushed the fix-cim-rehydration-without-native-mi branch from 4623748 to 0c41b87 Compare October 13, 2026 09:09
@jborean93

Jordan Borean (jborean93) commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

I encountered this problem when testing PSWSMan and I just patched out the CIM deserialization process on non-Windows client https://github.com/jborean93/PSWSMan/blob/main/src/PSWSMan/Patches/InternalDeserializer.cs#L15-L51. This patching is definitely not an option so would be great to see this fix landing in PowerShell and I can drop the patch.

I did also drop the __ClassMetadata and __InstanceMetadata from the PSObject as they seem to just be for rehydrating back to a live object and not actually related to the properties of the object and won't normally be present in a normally hydrated object. Not sure if you think that's the right idea here but thought it best to mention.

As for workarounds in the Invoke-Command case, the simplest is to just use Select-Object * (or whatever properties you need) inside the remote session.

Invoke-Command -HostName win-host { Get-CimInstance -ClassName Win32_Process -Filter "ProcessId=$pid" }
# OperationStopped: Unable to load shared library 'libmi' or one of its dependencies. In order to help diagnose loading problems, consider using a tool like strace. If you're using glibc, consider setting the LD_DEBUG environment variable:
# /home/jborean/.local/powershell/libmi.so: cannot open shared object file: No such file or directory
# /home/jborean/.local/powershell/liblibmi.so: cannot open shared object file: No such file or directory
# /home/jborean/.local/powershell/libmi: cannot open shared object file: No such file or directory
# /home/jborean/.local/powershell/liblibmi: cannot open shared object file: No such file or directory

Invoke-Command -HostName win-host { Get-CimInstance -ClassName Win32_Process -Filter "ProcessId=$pid" | Select-Object *}
# ProcessName                : pwsh.exe
# Handles                    : 523
# ...

This isn't great but it gets you moving forward if you need such objects back.

GitHub
Contribute to jborean93/PSWSMan development by creating an account on GitHub.

The serializer attaches __ClassMetadata and __InstanceMetadata as hidden
note properties, but the hidden flag is not serialized, so when the
instance falls back to the deserialized property bag the metadata showed
up in Select-Object * and Format-List *. Hide it again on every fallback
path. It is kept, so serializing the object again still carries it and a
receiver that can rehydrate CIM instances gets a CimInstance.
@Nova-Logic

Copy link
Copy Markdown
Author

Thanks, glad this would let you drop the patch in PSWSMan.

Good point about __ClassMetadata/__InstanceMetadata - with the fallback they showed up in Select-Object * and Format-List *. I'd rather not remove them though: they are what lets the object round-trip, e.g. Export-Clixml on macOS and Import-Clixml on Windows still gives you a live CimInstance.

The serializer already adds both as hidden note properties, only the hidden flag doesn't survive serialization. So f2033dd hides them again on every fallback path (including the existing CimException one). They no longer appear in Select-Object *, Format-List * or Get-Member (only with -Force), and serializing the object again still carries the metadata. The new test covers both.

And thanks for the Select-Object * workaround, that's a handy one until this ships.

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.

3 participants