AvaloniaUI/Avalonia · critical · Exception
Unable to obtain ANativeWindow
Error message
Unable to obtain ANativeWindow
What it means
Thrown by the AndroidFramebuffer constructor when the underlying InvalidationAwareSurfaceView exposes a zero (IntPtr.Zero) native window handle. The framebuffer needs a valid ANativeWindow pointer to lock and render pixels; a null handle means the Android Surface has not been created yet (or was already destroyed) at the time Avalonia tried to draw.
Source
Thrown at src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs:18
using System;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using Avalonia.Platform;
namespace Avalonia.Android.Platform.SkiaPlatform
{
unsafe class AndroidFramebuffer : ILockedFramebuffer
{
private IntPtr _window;
public AndroidFramebuffer(InvalidationAwareSurfaceView surface, double scaling)
{
if(surface == null)
throw new ArgumentNullException(nameof(surface));
_window = (surface as IPlatformHandle).Handle;
if (_window == IntPtr.Zero)
throw new Exception("Unable to obtain ANativeWindow");
ANativeWindow_Buffer buffer;
var rc = new ARect()
{
right = ANativeWindow_getWidth(_window),
bottom = ANativeWindow_getHeight(_window)
};
Size = new PixelSize(rc.right, rc.bottom);
ANativeWindow_lock(_window, &buffer, &rc);
(Format, AlphaFormat, RowBytes) = buffer.format == AndroidPixelFormat.WINDOW_FORMAT_RGB_565 ?
(PixelFormat.Rgb565, AlphaFormat.Opaque, buffer.stride * 2) :
(PixelFormat.Rgba8888, AlphaFormat.Premul, buffer.stride * 4);
Address = buffer.bits;
Dpi = new Vector(96, 96) * scaling;
}
View on GitHub (pinned to 11c5427268)
Solutions
- Ensure rendering is gated on the SurfaceWindowCreated event and suppressed on SurfaceWindowDestroyed — let InvalidationAwareSurfaceView drive the render loop, do not call render externally before surface creation.
- If embedding AvaloniaView, make sure it is attached to a visible window (added to the view hierarchy and laid out) before any draw is requested.
- Avoid forcing framebuffer locks during Activity.onPause/onResume transitions; defer rendering until the host signals the surface is valid.
- Upgrade Avalonia.Android — newer versions may short-circuit rendering when InternalView or its handle is invalid instead of throwing.
Example fix
// before
_topLevelImpl.Surfaces.OfType<IFramebufferPlatformSurface>().First().CreateFramebufferRenderTarget();
// after — only render when the surface is actually ready
if (_surfaceView.IsSurfaceValid)
{
_topLevelImpl.Surfaces.OfType<IFramebufferPlatformSurface>().First().CreateFramebufferRenderTarget();
} Defensive patterns
Strategy: validation
Validate before calling
// before locking the framebuffer, confirm the surface has a valid window handle var handle = (surfaceView as IPlatformHandle)?.Handle ?? IntPtr.Zero; if (handle == IntPtr.Zero) return; // surface not ready — skip this frame
Type guard
static bool HasValidNativeWindow(InvalidationAwareSurfaceView? view)
=> view is IPlatformHandle h && h.Handle != IntPtr.Zero; Try / catch
// framebuffer lock is internal; the actionable guard is to suppress rendering // when the surface isn't ready rather than catching after the fact. if (!HasValidNativeWindow(internalView)) return;
Prevention
- Drive rendering from the surface lifecycle events (SurfaceWindowCreated/Destroyed), not from external timers.
- Do not force a draw before the Activity's view is attached and laid out.
- Null/zero-check the surface handle before any render request in custom embedding code.
- Upgrade Avalonia.Android to pick up lifecycle-hardened render gating.
When it happens
Trigger: Constructing AndroidFramebuffer via FramebufferManager.Lock() when (surface as IPlatformHandle).Handle returns IntPtr.Zero. This happens if rendering is requested before SurfaceCreated fired, after SurfaceDestroyed fired, or if the SurfaceView was never properly attached to the window hierarchy.
Common situations: Activity paused/resumed lifecycle races where a draw is scheduled between SurfaceDestroyed and SurfaceCreated; embedding AvaloniaView in a ViewPager/RecyclerView where the view is detached; calling TopLevel.RequestAnimationFrame or forcing a render before the surface is ready; custom embedding that bypasses InvalidationAwareSurfaceView's lifecycle hooks.
Related errors
- Surface handle can't be 0x0
- TopLevel.InternalView was not expected to be null.
- SurfaceView.Holder was not expected to be null during Invali
- AndroidPlatformOptions.RenderingMode must not be empty or nu
- AndroidPlatformOptions.RenderingMode has a value of "{opts.R
AI-assisted analysis of AvaloniaUI/Avalonia@11c5427268 (2026-08-13).
Data as JSON: /api/errors/7a0ac62981f5c8ac.
Report an issue: GitHub.