microsoft/playwright-dotnet

[Bug]: Null reference exception in LocalUtils constructor

En3Tho opened this issue · 4 comments

Version

1.43-1.47

Steps to reproduce

Repro steps are somewhat convoluted:

  1. Use WSL + Docker for windows as container runtime
  2. Write a basic .net interactive notebook/script to create playwright instance and a browser e.g.
var playwright = await Microsoft.Playwright.Playwright.CreateAsync();
var browser = await playwright.Chromium.LaunchAsync(new()
{
    Headless = true,
    Args = [ "--start-maximized" ]
});
  1. Install and run dotnet repl global tool to run that notebook/script e.g. dotnet repl --run (Resolve-Path ./Playwright.dib)

Expected behavior

Instance of LocalUtils is created without exception or a meaningful exception is thrown

Actual behavior

NullReferenceException is thrown which is not really good or helpful

Additional context

Below is a full output from notebook. There is an important hint:
pw:channel:recv: {"guid":"","method":"__create__","params":{"type":"LocalUtils","initializer":{},"guid":"localUtils"}}
LocalUtils initializer comes empty and triggers a null-ref inside LocalUtils ctor:

public LocalUtils(ChannelOwner parent, string guid, LocalUtilsInitializer initializer) : base(parent, guid)
{
    foreach (var entry in initializer.DeviceDescriptors) // here DeviceDescriptors property is null
    {
        _devices[entry.Name] = entry.Descriptor;
    }
}

I do see a guard from missing LocalUtils here:

internal PlaywrightImpl(ChannelOwner parent, string guid, PlaywrightInitializer initializer)
     : base(parent, guid)
{
    _initializer = initializer;

    _devices = _connection.LocalUtils?._devices ?? new();
  ...
}

Does it mean that LocalUtils initilizer must come non-empty? If so then a proper check should be done on deserialization from json. Null reference exception simply indicates there is a bug but it doesn't describe a context why it is a bug.

Or can local utils initializer be actually empty? E.g. no devices (not sure what those are in context of playwright).

Output:

│ pw:channel:send:                                                                                                                                                   │
│ {"id":1,"guid":"","method":"initialize","params":{"sdkLanguage":"csharp"},"metadata":{"internal":false,"wallTime":1728897344483,"apiName":"Playwright.CreateAsync" │
│ ,"location":{"file":"C:\\projects\\dotnet-repl\\src\\dotnet-repl\\Repl.cs","line":66,"column":13}}}                                                                │
│ pw:channel:recv: {"guid":"","method":"__create__","params":{"type":"Android","initializer":{},"guid":"android@5fc151f23d559dfb3325fd0dfc2b9619"}}                  │
│ pw:channel:recv:                                                                                                                                                   │
│ {"guid":"","method":"__create__","params":{"type":"BrowserType","initializer":{"executablePath":"/root/.cache/ms-playwright/chromium-1033/chrome-linux/chrome","na │
│ me":"chromium"},"guid":"browser-type@4e2c07f2279ece98e381576716757f4d"}}                                                                                           │
│ pw:channel:recv:                                                                                                                                                   │
│ {"guid":"","method":"__create__","params":{"type":"BrowserType","initializer":{"executablePath":"/root/.cache/ms-playwright/firefox-1364/firefox/firefox","name":" │
│ firefox"},"guid":"browser-type@3084be799274cdb7b5f919436cb93625"}}                                                                                                 │
│ pw:channel:recv:                                                                                                                                                   │
│ {"guid":"","method":"__create__","params":{"type":"BrowserType","initializer":{"executablePath":"/root/.cache/ms-playwright/webkit-1735/pw_run.sh","name":"webkit" │
│ },"guid":"browser-type@55ed6eac0bae687d2fa5619e996cdcd6"}}                                                                                                         │
│ pw:channel:recv: {"guid":"","method":"__create__","params":{"type":"Electron","initializer":{},"guid":"electron@11da7c1ffaebb766de9990b25266217d"}}                │
│ pw:channel:recv: {"guid":"","method":"__create__","params":{"type":"LocalUtils","initializer":{},"guid":"localUtils"}}                                             │
│ Microsoft.Playwright.TargetClosedException: Object reference not set to an instance of an object.                                                                  │
│  ---> System.NullReferenceException: Object reference not set to an instance of an object.                                                                         │
│    at Microsoft.Playwright.Core.LocalUtils..ctor(ChannelOwner parent, String guid, LocalUtilsInitializer initializer) in /_/src/Playwright/Core/LocalUtils.cs:line │
│ 40                                                                                                                                                                 │
│    at Microsoft.Playwright.Transport.Connection.CreateRemoteObject(String parentGuid, ChannelOwnerType type, String guid, Nullable`1 initializer) in               │
│ /_/src/Playwright/Transport/Connection.cs:line 365                                                                                                                 │
│    at Microsoft.Playwright.Transport.Connection.Dispatch(PlaywrightServerMessage message) in /_/src/Playwright/Transport/Connection.cs:line 280                    │
│    --- End of inner exception stack trace ---                                                                                                                      │
│    at Microsoft.Playwright.Transport.Connection.InnerSendMessageToServerAsync[T](ChannelOwner object, String method, Dictionary`2 dictionary, Boolean keepNulls)   │
│ in /_/src/Playwright/Transport/Connection.cs:line 206                                                                                                              │
│    at Microsoft.Playwright.Transport.Connection.WrapApiCallAsync[T](Func`1 action, Boolean isInternal) in /_/src/Playwright/Transport/Connection.cs:line 526       │
│    at Microsoft.Playwright.Transport.Connection.InitializePlaywrightAsync() in /_/src/Playwright/Transport/Connection.cs:line 245                                  │
│    at Microsoft.Playwright.Playwright.CreateAsync() in /_/src/Playwright/Playwright.cs:line 64                                                                     │
│    at Submission#5.<<Initialize>>d__0.MoveNext()

Environment

  • Operating System: WSL/ Ubuntu 22.04
  • CPU: x64
  • .NET Version (TFM): net8.0

It looks like you still have some very old Playwright cached somewhere, since the protocol logs show that its offering Chromium 1033 which corresponds to Playwright 1.28 while you connect with a more modern Playwright (1.43ish). So maybe try to purge some .NET interactive caches? Most likely its some old bin/obj folder somewhere.

I wonder where it is coming from because I do have chromium-1134 ffmpeg-1010 in /root/.cache/ms-playwright. I did a clean install of playwright@1.47 and it is still weirdly reporting that. It is also happening when installing using a dontet build + install.ps1 combination. I wonder if I do in fact have some garbage cached in docker itself that somehow gets into container

I'm unfortunately not familiar with dotnet-repl, it seems to be using Microsoft.DotNet.Interactive.CSharp - so maybe it has a cache somewhere.

I'd try deleting ~/.nuget/packages as per here. Are you able to share a full reproduction? Ideally a Dockerfile which we can run? Does it reproduce there?

@mxschmitt Sorry for the delayed response. You were right: dotnet repl bundles an old playwright version. Using driver search path env variable fixed the issue.

It is also worth noting that dotnet repl has lots of problems and I ended up using dotnet script instead as it's much more stable and easier to work with