dotnet/runtime · warning
mono/marshal: SafeHandles missing MANAGED_CONV_IN\n
Error message
mono/marshal: SafeHandles missing MANAGED_CONV_IN\n
What it means
In Mono's marshal-ilgen, emit_marshal_safehandle_ilgen handles the SafeHandle marshalling IL emission for P/Invoke stubs. The MARSHAL_ACTION_MANAGED_CONV_IN case (reverse P/Invoke: native calls a managed delegate that takes a SafeHandle) is unimplemented: it only prints this message to stderr and emits no IL. This indicates that SafeHandle handling for native->managed 'in' direction is incomplete in this runtime build.
Source
Thrown at src/mono/mono/component/marshal-ilgen.c:2038
}
/* Store the IntPtr results into a local */
intptr_handle_slot = cb_to_mono->methodBuilder.add_local (mb, int_type);
cb_to_mono->methodBuilder.emit_stloc (mb, intptr_handle_slot);
/* Create return value */
cb_to_mono->methodBuilder.emit_op (mb, CEE_NEWOBJ, ctor);
cb_to_mono->methodBuilder.emit_stloc (mb, 3);
/* Set the return.handle to the value, am using ldflda, not sure if thats a good idea */
cb_to_mono->methodBuilder.emit_ldloc (mb, 3);
cb_to_mono->methodBuilder.emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoSafeHandle, handle));
cb_to_mono->methodBuilder.emit_ldloc (mb, intptr_handle_slot);
cb_to_mono->methodBuilder.emit_byte (mb, CEE_STIND_I);
break;
}
case MARSHAL_ACTION_MANAGED_CONV_IN:
fprintf (stderr, "mono/marshal: SafeHandles missing MANAGED_CONV_IN\n");
break;
case MARSHAL_ACTION_MANAGED_CONV_OUT:
fprintf (stderr, "mono/marshal: SafeHandles missing MANAGED_CONV_OUT\n");
break;
case MARSHAL_ACTION_MANAGED_CONV_RESULT:
fprintf (stderr, "mono/marshal: SafeHandles missing MANAGED_CONV_RESULT\n");
break;
default:
printf ("Unhandled case for MarshalAction: %d\n", action);
}
return conv_arg;
}
static int
emit_marshal_handleref_ilgen (EmitMarshalContext *m, int argnum, MonoType *t,
MonoMarshalSpec *spec, int conv_arg,View on GitHub (pinned to 60108ba66e)
Solutions
- Avoid SafeHandle parameters on delegates called back from native code; marshal the underlying IntPtr handle and wrap it manually on the managed side.
- Use a plain IntPtr or HandleRef for the reverse-P/Invoke boundary, then construct/Release the SafeHandle in managed code.
- If you control the native ABI, pass raw handle integers and do lifetime management explicitly.
- Upgrade to a .NET runtime (CoreCLR/NativeAOT) that implements the SafeHandle managed-conv path for reverse invocations.
Example fix
// before
public delegate int NativeCallback(SafeFileHandle h);
[DllImport("n")] static extern void register(NativeCallback cb);
// after
public delegate int NativeCallback(IntPtr h);
[DllImport("n")] static extern void register(NativeCallback cb);
// in the callback, wrap h in a SafeFileHandle manually and own its lifetime Defensive patterns
Strategy: validation
Validate before calling
// Validate at startup that no reverse-P/Invoke delegate carries a SafeHandle param,
// since Mono's MANAGED_CONV_IN for SafeHandle is unimplemented.
static IEnumerable<MethodInfo> CheckReversePInvokes(Assembly asm) {
foreach (var t in asm.GetTypes())
foreach (var m in t.GetMethods(BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Static|BindingFlags.Instance)) {
var dlg = m.GetCustomAttribute<UnmanagedFunctionPointerAttribute>();
if (dlg == null) continue;
foreach (var p in m.GetParameters())
if (typeof(SafeHandle).IsAssignableFrom(p.ParameterType))
yield return m; // will hit the unimplemented Mono path
}
} Prevention
- Do not use SafeHandle on delegates invoked from native code on Mono; use IntPtr.
- Run an assembly scan at build time to flag reverse-P/Invoke signatures using SafeHandle.
- Keep SafeHandle usage on the managed->native direction only.
- Target CoreCLR/NativeAOT if you need full SafeHandle reverse marshalling.
When it happens
Trigger: A managed delegate with a SafeHandle parameter is passed to native code and invoked from native (reverse P/Invoke), or a COM/WinRT interop path triggers the MANAGED_CONV_IN action for a SafeHandle-typed parameter. The marshaller reaches this case and emits a no-op instead of conversion IL.
Common situations: Using [DllImport] reverse pinvokes or Marshal.GetDelegateForFunctionPointer with SafeHandle arguments on Mono; COM interop scenarios where a SafeHandle crosses the managed boundary inbound; running code that works on CoreCLR but exercises an unimplemented Mono marshaller path.
Related errors
- mono/marshal: SafeHandles missing MANAGED_CONV_OUT\n
- mono/marshal: SafeHandles missing MANAGED_CONV_RESULT\n
- Unhandled case for MarshalAction: %d\n
- Locale or culture name is null or empty. localeName=${locale
- ${part} not found while looking up ${function_name}
AI-assisted analysis of dotnet/runtime@60108ba66e (2026-08-10).
Data as JSON: /api/errors/46617de7a7905863.
Report an issue: GitHub.