Skip to content

Fix encoding 866 crash: register CodePagesEncodingProvider + SharedIO fallback - #851

Open
dronov-dmitry wants to merge 1 commit into
IronLanguages:masterfrom
dronov-dmitry:fix/encoding-866-fallback
Open

Fix encoding 866 crash: register CodePagesEncodingProvider + SharedIO fallback#851
dronov-dmitry wants to merge 1 commit into
IronLanguages:masterfrom
dronov-dmitry:fix/encoding-866-fallback

Conversation

@dronov-dmitry

@dronov-dmitry dronov-dmitry commented Sep 10, 2026

Copy link
Copy Markdown

Problem

Python.CreateEngine() crashes with:

InvalidOperationException:
  Failed to load language 'IronPython 2.7.12':
    No data is available for encoding 866.

This happens in hosting environments like Dynamo (Autodesk Revit) or on .NET Core where cp866 encoding is not registered by default.

Root cause

SharedIO.InitializeInput() accesses Console.InputEncoding without error handling. On Russian Windows this returns cp866, but in certain hosting environments the codepage data is unavailable, causing ArgumentException.

Fix (2 parts)

Part 1: DLR — SharedIO fallback

File: Src/DLR/Src/Microsoft.Scripting/Runtime/SharedIO.cs

Added TryGetConsoleEncoding() helper that wraps console encoding access in try-catch with UTF-8 fallback:

private static Encoding TryGetConsoleEncoding(Func<Encoding> getter, Encoding fallback) {
    try { return getter(); } catch { return fallback; }
}

Applied to:

  • InitializeInput()Console.InputEncoding → fallback Encoding.UTF8
  • InitializeOutput()Console.Out → fallback TextWriter.Null
  • InitializeErrorOutput()Console.Error → fallback TextWriter.Null

Full diff for DLR submodule:

diff --git a/Src/Microsoft.Scripting/Runtime/SharedIO.cs b/Src/Microsoft.Scripting/Runtime/SharedIO.cs
index 4087314..8c4be89 100644
--- a/Src/Microsoft.Scripting/Runtime/SharedIO.cs
+++ b/Src/Microsoft.Scripting/Runtime/SharedIO.cs
@@ -87,16 +87,24 @@
         internal SharedIO() {
         }
 
+        private static Encoding TryGetConsoleEncoding(Func<Encoding> getter, Encoding fallback) {
+            try {
+                return getter();
+            } catch {
+                return fallback;
+            }
+        }
+
         private void InitializeInput() {
             if (_inputStream == null) {
                 lock (_mutex) {
                     if (_inputStream == null) {
 #if FEATURE_FULL_CONSOLE
                         _inputStream = ConsoleInputStream.Instance;
-                        _inputEncoding = Console.InputEncoding;
+                        _inputEncoding = TryGetConsoleEncoding(() => Console.InputEncoding, Encoding.UTF8);
                         _inputReader = Console.In;
 #elif FEATURE_BASIC_CONSOLE
-                        _inputEncoding = Encoding.Default;
+                        _inputEncoding = TryGetConsoleEncoding(() => Encoding.Default, Encoding.UTF8);
                         _inputStream = new TextStream(Console.In, _inputEncoding);
                         _inputReader = Console.In;
 #else
@@ -115,7 +123,11 @@
                     if (_outputStream == null) {
 #if FEATURE_FULL_CONSOLE
                         _outputStream = Console.OpenStandardOutput();
-                        _outputWriter = Console.Out;
+                        try {
+                            _outputWriter = Console.Out;
+                        } catch {
+                            _outputWriter = TextWriter.Null;
+                        }
 #elif FEATURE_BASIC_CONSOLE
                         _outputStream = new TextStream(Console.Out);
                         _outputWriter = Console.Out;
@@ -134,7 +146,11 @@
                     if (_errorStream == null) {
 #if FEATURE_FULL_CONSOLE
                         _errorStream = Console.OpenStandardError();
-                        _errorWriter = Console.Error;
+                        try {
+                            _errorWriter = Console.Error;
+                        } catch {
+                            _errorWriter = TextWriter.Null;
+                        }
 #elif FEATURE_BASIC_CONSOLE
                         _errorStream =  new TextStream(Console.Error);
                         _errorWriter = Console.Error;

Part 2: IronPython — RegisterProvider (this PR)

File: Src/IronPython/Runtime/PythonContext.cs

Added static constructor that registers CodePagesEncodingProvider for .NET Core/Standard:

static PythonContext() {
    try {
#if NETCOREAPP || NETSTANDARD
        Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
#endif
    } catch { }
}

File: Src/IronPython/IronPython.csproj

Added System.Text.Encoding.CodePages package for non-net45 targets:

<ItemGroup Condition=" '\$(TargetFramework)' != 'net45' ">
    <PackageReference Include="System.Text.Encoding.CodePages" Version="4.6.0" />
</ItemGroup>

Testing

All tests pass (C# host simulating Dynamo + IronPython scripts):

[PASS] Python.CreateEngine()
[PASS] Script execution: x = 2 + 2 -> 4
[PASS] Cyrillic: привет = 'мир'
[PASS] groupBy from Processing_List_eng -> [(1,2),(2,3),(3,4)]
[PASS] External .py file execution
[PASS] Roundtrip encode/decode: utf-8, cp1251, iso-8859-1

Compatibility

  • .NET Framework 4.5+ (codepages built-in, SharedIO fallback as safety net)
  • .NET Core 3.1+ (RegisterProvider + SharedIO fallback)
  • .NET Standard 2.0 (RegisterProvider + SharedIO fallback)

…em.Text.Encoding.CodePages

Problem:
Python.CreateEngine() crashes with:
  'Failed to load language IronPython 2.7.12: No data is available for encoding 866'

This happens because SharedIO.InitializeInput() accesses Console.InputEncoding
which returns cp866 on Russian Windows. In Dynamo/Revit hosting environment
or on .NET Core, cp866 is not registered, causing ArgumentException.

Fix:
1. PythonContext static constructor: register CodePagesEncodingProvider
   for NETCOREAPP/NETSTANDARD targets
2. IronPython.csproj: add System.Text.Encoding.CodePages package for
   non-net45 targets (net45 has codepages built-in)

Note: The DLR submodule (Src/DLR) also needs a corresponding fix in
SharedIO.cs to wrap Console.InputEncoding/Output access in try-catch
with UTF-8 fallback. That fix is in a separate commit in the DLR repo.
@dronov-dmitry

Copy link
Copy Markdown
Author

@dotnet-policy-service agree

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