ScreenshotNeo

BlogHow-to

Why Selenium WebDriver Screenshots Fail to Load in ExtentReports C#

Fix missing Selenium screenshots in ExtentReports C#: verify capture, attachment APIs, paths, Base64, versions, and report packaging.

By the ScreenshotNeo team1 October 20267 min read

Direct answer: troubleshoot this in layers. First prove that Selenium saved a valid PNG. Then verify that your ExtentReports API matches your installed major version, that you attached the image to the intended test or log entry, and that the generated HTML can resolve the referenced file. If the file reference is fragile, attach Selenium’s Base64 image instead.

1. Verify Selenium captured and saved the image

Selenium’s .NET API returns a Screenshot object. The documented C# sequence is to cast the driver to ITakesScreenshot, call GetScreenshot(), and call SaveAsFile with a full path. Selenium documents this as writing PNG data.

using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using System;
using System.IO;

var options = new ChromeOptions();
using var driver = new ChromeDriver(options);
driver.Navigate().GoToUrl("https://example.com");

var directory = Path.Combine(AppContext.BaseDirectory, "artifacts", "screenshots");
Directory.CreateDirectory(directory);
var fullScreenshotPath = Path.Combine(directory, "example.png");

var screenshot = ((ITakesScreenshot)driver).GetScreenshot();
screenshot.SaveAsFile(fullScreenshotPath);

Console.WriteLine($"Screenshot path: {fullScreenshotPath}");
Console.WriteLine($"Exists: {File.Exists(fullScreenshotPath)}");
Console.WriteLine($"Bytes: {new FileInfo(fullScreenshotPath).Length}");

Open that PNG outside the report. Also log the exact path, check that the file is non-empty, and capture while the browser session and target page are still in the required state. A PNG that cannot be opened is a Selenium, driver, timing, permissions, or destination problem; it is not yet an ExtentReports display problem.

2. Attach the image using the correct ExtentReports API

ExtentReports v4 has separate APIs for attaching a path to a test and attaching media to a log event. Check your NuGet package and reporter before copying examples. ExtentReports v5 changed parts of the reporter surface, so a v4 example may not compile or may not match your configured reporter.

Attach to the test

test.AddScreenCaptureFromPath(fullScreenshotPath);

Attach to a failure log entry

test.Fail(
    "Failure details",
    MediaEntityBuilder
        .CreateScreenCaptureFromPath(fullScreenshotPath)
        .Build());

The second form matters when the screenshot should appear beside a particular log event. Calling only the test-level method does not create a media entity for a separate log message.

Complete v4-style failure example

using AventStack.ExtentReports;
using AventStack.ExtentReports.Reporter;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using System;
using System.IO;

var reportDirectory = Path.Combine(AppContext.BaseDirectory, "artifacts", "report");
var screenshotDirectory = Path.Combine(reportDirectory, "screenshots");
Directory.CreateDirectory(screenshotDirectory);

var report = new ExtentReports();
report.AttachReporter(new ExtentHtmlReporter(Path.Combine(reportDirectory, "index.html")));
var test = report.CreateTest("Example page");

using var driver = new ChromeDriver();
try
{
    driver.Navigate().GoToUrl("https://example.com");
    test.Pass("Page loaded");
}
catch (Exception ex)
{
    var path = Path.Combine(screenshotDirectory, "example-failure.png");
    var image = ((ITakesScreenshot)driver).GetScreenshot();
    image.SaveAsFile(path);

    test.Fail(ex.Message,
        MediaEntityBuilder.CreateScreenCaptureFromPath(path).Build());
}
finally
{
    report.Flush();
    driver.Quit();
}

The ExtentReports v4 documentation explains that file screenshots are saved on disk and referenced in the report with an <img> element. Therefore, a screenshot existing on the test machine does not prove that a copied or published report can still resolve it.

3. Make the report and screenshot layout portable

Use one stable artifact directory and publish the HTML together with its screenshot folder:

artifacts/
  report/
    index.html
    screenshots/
      example-failure.png

After generation, inspect the report’s HTML and find the actual image src. Do not assume that every reporter resolves relative paths from the executable directory, source directory, or report directory. Confirm what the generated HTML contains in your environment.

  1. Create the screenshot directory before saving.
  2. Use a full path when calling SaveAsFile.
  3. Copy the screenshot directory whenever you copy the report.
  4. When publishing CI artifacts, include both index.html and the image files.
  5. Open the copied artifact, not only the original workspace report.
  6. Check for path characters, case differences, and cleanup jobs that delete images after the report is generated.

4. Use Base64 when a separate path is the failure point

ExtentReports v4 documents Base64 overloads for both test-level media and log entries. Selenium exposes the encoded image through AsBase64EncodedString.

var imageBase64 = ((ITakesScreenshot)driver)
    .GetScreenshot()
    .AsBase64EncodedString;

test.Fail(
    "Failure details",
    MediaEntityBuilder
        .CreateScreenCaptureFromBase64String(imageBase64)
        .Build());

This embeds the image data in the report event, so rendering no longer depends on a separate PNG path. Pass the raw Base64 payload expected by the ExtentReports overload; do not prepend a data:image/png;base64, header unless the specific API documents that format. Selenium documents invalid encoded data as a possible FormatException when constructing a screenshot from Base64.

Attachment Report uses Validate
File path Reference to a PNG on disk Path resolution, file existence, and artifact copying
Base64 Encoded image content in the event Correct overload and unmodified Base64 value

5. Check the installed ExtentReports version

Before changing code, inspect the actual package version in the project file, lock file, or NuGet output. The examples above follow the documented ExtentReports .NET v4 APIs. ExtentReports v5 removed some older reporters and has different compatibility considerations. Confirm the reporter type and consult the documentation for that exact major version.

dotnet list package | findstr ExtentReports

If a method is missing, first treat it as a version mismatch. Do not silently copy an old reporter name into a v5 project.

6. A repeatable diagnostic checklist

  1. Capture: does GetScreenshot() return while the driver is alive?
  2. Write: does SaveAsFile complete without an exception?
  3. Inspect: can the PNG open independently, and is its byte length greater than zero?
  4. Attach: did you use the test-level or log-entry API that matches where you expect the image?
  5. Generate: did you call Flush() after adding the media?
  6. Resolve: does the generated HTML point to the real image location?
  7. Publish: did the CI artifact include the image directory?
  8. Version: do the package and reporter match the API example?
  9. Fallback: does the Base64 overload render the same screenshot?

7. Common errors and fixes

Symptom Likely cause Fix
No PNG is created Invalid driver state, premature teardown, missing directory, or write permission Capture before quitting the driver, create the directory, log the full path, and check the exception.
PNG opens locally but is blank in the report Report references a missing or different file Inspect the generated HTML src and publish the image beside the report.
Image appears nowhere Media was attached to a different object than the visible log event Use AddScreenCaptureFromPath for test-level media or MediaEntityBuilder for a log entry.
Method does not compile ExtentReports major-version or reporter mismatch Check the installed package and use documentation for that version.
Works in CI but not after download Artifact contains HTML without referenced screenshots Archive the complete report directory, preserving relative paths.
Base64 overload throws Wrong value or a data-URI prefix where raw Base64 is expected Use AsBase64EncodedString directly and select the matching overload.
Report has no latest changes Report was opened before the final flush Call Flush() after all tests and attachments are complete.

8. Reliability and performance considerations

Capture at the failure point, before navigation or teardown changes the page. Give each test a unique filename when tests can run in parallel; otherwise one test can overwrite another test’s image. Keep the report and screenshot directory in the same artifact root so cleanup and publishing treat them as one unit.

Path references keep image bytes outside the HTML, while Base64 makes the report self-contained. The cited documentation does not provide a universal speed or size benchmark, so choose based on portability and artifact handling rather than an assumed performance advantage. For very large suites, monitor report and artifact size and retain only the screenshots needed for diagnosis.

9. Or skip the browser setup

For URL screenshots that do not need your own Selenium session, ScreenshotNeo provides a single GET request and supports PNG, JPEG, WebP, and PDF output. Its consent step accepts cookie banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be disabled. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. The service also has an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.

See the ScreenshotNeo API documentation for authentication and options.

cURL

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo includes full-page and element captures, device presets or custom viewports, retina scale, dark mode, custom CSS and JavaScript, selector waits, delays, network-idle waits, headers, cookies, user agents, authorization, timezone, geolocation, blocking controls, resizing, caching with a chosen TTL, signed links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs per call, usage reporting, and an OpenAPI specification. Every feature is on every plan. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.

Create a free ScreenshotNeo account and get 1,000 screenshots a month with no card.

10. FAQ

Does a visible browser screenshot prove ExtentReports is configured correctly?

No. It proves capture and file writing. The report still needs a valid attachment call and a resolvable path or valid Base64 payload.

Should every project switch to Base64?

No. Base64 is useful when reports move between machines. Path references are suitable when your artifact layout reliably preserves the image files.

Why does the test-level attachment differ from a log attachment?

They are separate ExtentReports APIs. A log event needs a media entity built with MediaEntityBuilder.

Can this guide identify the exact path base directory?

No. The report’s generated HTML and your artifact layout are the source of truth for path resolution in a particular reporter and environment.