Loki Screenshot Tests Fail in GitHub Actions: Common Fixes
Diagnose Loki failures in GitHub Actions by checking Storybook readiness, browser parity, baselines, rendering stability, and workflow logs.
If Loki screenshot tests fail in GitHub Actions, first identify the exact failed workflow step and inspect its logs. Confirm Storybook is already running and ready before Loki starts; then compare the Loki and Storybook versions, browser target, runner image, reference images, rendering readiness, animation state, and concurrency between CI and local runs. Save screenshots and test output as workflow artifacts so you can inspect the failure before changing or approving baselines.
The title alone does not identify the cause. Use the sequence below to isolate it, changing one variable at a time. Check the requirements for the Loki version your project has pinned: the documented getting-started page lists Node 16+, Docker as an optional dependency for chrome.docker, and Chrome 59+ for chrome.app; these are versioned documentation facts, not guarantees for every release. Loki getting started
1. Identify which workflow step failed
Open the failed GitHub Actions run and expand the failed step. Determine whether the failure occurred while installing dependencies, starting Storybook, launching the browser, running Loki, or comparing screenshots. Fixing baselines will not resolve a server startup or browser launch error.
GitHub-hosted runner setup logs include runner-image details and a link to preinstalled tools. Record those details alongside the failing step. If ordinary logs do not show enough, enable workflow debug logging or Loki’s verbose output where available in your pinned version. GitHub workflow run logs
Record these details before changing anything
- Loki and Storybook versions from the lockfile and installed project.
- The configured Loki browser target and browser or container version.
- The GitHub runner image, operating environment, and Chrome concurrency.
- The command and step that failed, plus the complete relevant error output.
- The Storybook URL, whether it was reachable and ready, and the workflow revision.
- Whether the reference images exist on that revision and whether local and CI used the same references.
- Which stories fail, and whether they load data asynchronously or animate.
2. Make sure Storybook is running before Loki
Loki does not start Storybook for you. The server must be running before you execute Loki, and the browser must be able to reach the configured Storybook URL. Loki’s README explicitly says to ensure Storybook and any simulator or emulator are up before running tests. Loki repository README
Check the workflow order. A common sequence is: install dependencies, start Storybook, wait for its server to become available, then run Loki. The exact command and readiness mechanism depend on your project; use the scripts and port configured by your repository rather than copying assumed values.
# Illustrative workflow order; replace script names with your project's scripts.
- run: yarn install --frozen-lockfile
- run: yarn storybook --ci &
- run: yarn loki test
Do not treat a successful background start command as proof the site is ready. If Loki reports connection refused, navigation failures, or a blank page, inspect the Storybook process output and verify the URL from the runner. A readiness wait should fail clearly if the server never becomes available, rather than allowing Loki to race the startup.
3. Check browser target and local-to-CI parity
Loki’s repository recommends Chrome in Docker. Its documented targets also include local Chrome, AWS Lambda, iOS Simulator, and Android Emulator. Confirm the workflow uses the intended target and that it matches the project configuration. Loki configuration
Compare local and CI on the same axes: Loki and Storybook versions, browser target and browser or container version, runner image, operating environment, Storybook readiness and URL, reference revision, story data and readiness, animation state, and Chrome concurrency. A community discussion describes local-versus-CI element-position differences and suggests matching Docker/browser setup or reducing chromeConcurrency to 1 or 2. That is anecdotal advice, not a universal fix; test it as one controlled variable. Loki community discussions
Loki aims for reproducible tests across operating systems, and recommends Docker Chrome, but that aim does not guarantee pixel-identical output in every custom environment. Pin relevant dependencies and compare the actual CI runner and browser details before attributing a difference to an application change.
4. Inspect references before updating them
Loki’s documented workflow creates reference images with yarn loki update, runs comparisons with yarn loki test, and asks you to review current and difference images. References are ordinarily checked into the repository; the documentation also mentions Git LFS as an option. Loki documentation
- Confirm the expected reference images are present on the checked-out commit and available to the workflow.
- Inspect the current screenshot and diff for each failed story.
- Decide whether the visual change is intended. If it is, regenerate references in a controlled update and review the resulting files.
- Commit the reviewed references so later CI runs compare against the intended revision.
Do not automatically approve references after a failed run. A baseline update can hide an actual regression if you have not reviewed the images.
5. Stabilize stories that render asynchronously
A story that fetches data, waits on a component, or rerenders asynchronously can be captured before it reaches its intended state. Loki’s flaky-test documentation describes this failure mode and documents @loki/create-async-callback as a way for a story to signal that it is ready. Follow the documented pattern for the Loki version in your project. Loki flaky tests
Make the screenshot’s target state deterministic: use controlled story data, make readiness explicit when rendering is asynchronous, and avoid relying on timing guesses when the story can signal completion. If only a subset of stories is flaky, compare their data loading and rerender behavior with a stable story instead of changing the entire suite.
6. Handle animations and moving content
Loki disables common CSS transitions and animations and requestAnimationFrame by default, but its documentation lists limitations: looped requestAnimationFrame, GIFs, SVG animations, native Lottie, and React Native’s Animated library may need project-specific handling. Disable or freeze those effects for visual tests when the story should represent a static frame. If a story cannot sensibly be tested as a static image, Loki documents skipping it with loki: { skip: true }. Loki flaky tests
7. Preserve evidence from failed runs
Upload relevant logs, screenshots, and test output as GitHub Actions artifacts. Artifacts let you retain files produced by a run and inspect or share them after the job completes; GitHub’s documentation includes test results and screenshots as examples. GitHub workflow artifacts
# Example artifact step. Adjust paths to match your Loki output and project.
- name: Upload visual test output
if: always()
uses: actions/upload-artifact@v4
with:
name: loki-debug-output
path: |
**/loki/**
**/screenshots/**
**/test-results/**
if-no-files-found: ignore
Artifact paths must match where your project actually writes files. Keep the if: always() condition so the upload step can run after a preceding step fails. Consult the action documentation for available options and retention behavior.
8. Use a controlled comparison to isolate the cause
- Re-run the failing commit with the same lockfile and references, preserving logs and screenshots.
- Compare the recorded environment axes from the local and CI runs.
- Choose one plausible difference, such as browser target, runner image, readiness, or concurrency, and change only that factor.
- Repeat the failing story and compare its current image and diff with the original run.
- Keep the change only if the evidence connects it to the failure; otherwise restore it and test the next variable.
- Update references only after reviewing and accepting the visual change.
This approach distinguishes environment drift from a real UI change and from nondeterministic story output. Without the failing step and run details, there is no reliable way to name one fix in advance.
Common errors and fixes
| Symptom | Likely area to check | Targeted action |
|---|---|---|
| Connection refused, navigation error, or empty Storybook page | Server not started, wrong URL, or Loki started before Storybook was ready | Inspect the Storybook step and URL from the runner; wait for readiness before Loki runs. |
| Browser or Docker launch failure | Configured target, runner environment, or version-specific prerequisite | Check Loki’s configuration and getting-started documentation for the pinned release; inspect runner setup logs. |
| Images differ only in CI | Browser/container version, runner image, target mismatch, or concurrency | Record local and CI environments and vary one factor at a time. Treat concurrency advice as a diagnostic experiment. |
| Only stories with data or delayed rendering fail | Screenshot occurs before asynchronous rendering is complete | Make story readiness explicit using Loki’s documented async callback pattern. |
| Animated stories produce inconsistent images | Animation type is not disabled by Loki’s defaults | Disable or freeze the effect for visual checks, or skip a story that has no meaningful static frame. |
| Unexpected broad set of image diffs | Reference images missing, stale, or from a different revision; environment changed | Check out the intended references and compare runner/browser details before approving updates. |
| Logs do not explain the failure | Insufficient workflow or tool diagnostics | Enable GitHub workflow debug logging or Loki verbose output, then preserve output as an artifact. |
| Artifact step finds no files | Configured artifact paths do not match actual output locations | Inspect the workspace and Loki output paths, then adjust the upload patterns. |
Performance, reliability, and cost considerations
For reliability, prioritize consistent pinned dependencies, an explicitly ready Storybook server, a stable browser target, deterministic story state, and reviewed baselines. Record runner-image changes because hosted environments can change; consult the current run’s setup logs rather than assuming the same preinstalled tools are present.
For performance, concurrency is a diagnostic and configuration variable: lowering it can help determine whether parallel browser work is involved, but increases elapsed time. Choose a value based on repeatable results in your environment. Avoid adding arbitrary waits to every story when only specific stories need a readiness signal.
GitHub Actions usage and cost depend on the workflow and runner arrangement; the research sources provide no price or benchmark for this particular Loki workflow. Keep diagnostic artifacts focused on useful logs and images, and consult your GitHub plan and retention settings for actual costs and limits. Loki itself compares browser screenshots; a general screenshot API does not replace its reference-image workflow or visual diff checks.
Or skip the browser setup
For a one-off page capture or a separate capture task, ScreenshotNeo offers a screenshot API and MCP server. It is not a replacement for Loki’s Storybook baselines and visual regression comparisons. One GET request can return a PNG, JPEG, WebP, or PDF. See the ScreenshotNeo API documentation for request options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const image = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', image));
- Cookie banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off.
- Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed; response headers report the page verdict and billing status.
- An MCP server lets AI agents use
take_screenshot,get_page_info, andcapture_pdf. - 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000 shots. Learn about ScreenshotNeo
Sign up for 1,000 free screenshots a month, with no card.
FAQ
Should I run loki update whenever CI fails?
No. First inspect the current images and diff. Update references only when you confirm the visual change is intended.
Does matching operating systems guarantee identical screenshots?
No. Compare the browser target and version, runner image, readiness, references, story state, and concurrency as well. Loki’s reproducibility goal does not guarantee pixel identity in every setup.
Can ScreenshotNeo run my Loki tests?
No. ScreenshotNeo captures web pages through an API or MCP server; Loki’s Storybook reference-image and comparison workflow remains a separate tool.
What details are needed to diagnose one specific failure?
The failed-step output, Loki and Storybook versions, target, runner image, workflow configuration, Storybook readiness, reference revision, and the affected story’s rendering behavior.


