afterbuild/ops
ERR-895/stack trace
ERR-895
Claude Code tests pass local, fail CI

Claude Code tests pass local, fail CI

Last updated 15 April 2026 · 8 min read · By Hyder Shah
Direct answer

Claude Code tests that pass locally and fail in CI are almost always one of four things: implicit timezone / locale assumptions (your machine is America/New_York, CI is UTC), env vars missing in the CI runner, test isolation / order dependency (shared DB or global state), or Node / Python version drift between your machine and the CI image. Each has a 5-minute diagnostic and a 30-minute fix.

Quick fix for Claude Code tests pass local, fail

Start here

Fix 1 — Pin the clock and timezone in tests

Add a global test setup that fixes TZ and freezes Date:

// vitest.setup.ts
import { beforeEach, afterEach, vi } from "vitest";
process.env.TZ = "UTC";
beforeEach(() => {
  vi.useFakeTimers();
  vi.setSystemTime(new Date("2026-04-15T12:00:00Z"));
});
afterEach(() => vi.useRealTimers());

Ask Claude Code to grep for any remaining new Date() calls in source code and replace them with an injected clock dependency. That makes the tests deterministic across machines.

Deeper fixes when the quick fix fails

  1. 02

    Fix 2 — Supply every env var to CI explicitly

    Inventory required env vars by validating them at app boot (Zod, envsafe, or a simple check). In .github/workflows/test.yml, list every variable under env:with a CI-safe value — for tests, use a dedicated test database URL, a dummy OPENAI_API_KEY, a fixed NEXTAUTH_SECRET.

    env:
      DATABASE_URL: postgres://postgres@localhost/test
      OPENAI_API_KEY: test-key
      NODE_ENV: test

    Commit a .env.exampleas the source of truth so new engineers and Claude Code both know what’s required.

  2. 03

    Fix 3 — Enforce test isolation

    If a test passes in isolation but fails when the full suite runs, you have shared state. Common culprits:

    • Shared database rows — wrap each test in a transaction that rolls back.
    • Module-level singletons — reset between tests with vi.resetModules().
    • File-system writes — use a temp dir per test, cleaned in afterEach.
    • Mock handlers leaking — reset handlers (msw.restoreHandlers()).

    Run the suite in random order (vitest --sequence.shuffleor jest --randomize). Any new failure means an implicit order dependency you hadn’t noticed.

  3. 04

    Fix 4 — Pin Node, Python, and key binaries everywhere

    Claude Code does not read your Node version before writing code. If you’re on Node 22 and CI is on Node 18, any new syntax or API breaks.

    Pin in three places:

    • package.json engines field
    • .nvmrc or .tool-versions at repo root
    • CI workflow actions/setup-node with node-version-file

    Same pattern for Python: pyproject.toml requires-python, .python-version, and CI setup-python.

A robust CI for a Claude Code project is non-negotiable. Without it, every prompt is a gamble — industry benchmarks (see our 2026 research) put AI-code vulnerability rates close to half, and most of those only surface with a working test suite.

Why AI-built apps hit Claude Code tests pass local, fail

Claude Code writes tests that pass in the environment it sees. If you run npm test on a machine with a filled .env, a seeded database, and your own timezone, the test passes. CI is a clean machine in UTC with no .env and a freshly created database. Anything implicit about your local environment becomes a CI failure.

The second half of the problem is that Claude Code rarely writes tests with explicit environmental assertions. Tests that use new Date() and Intl.DateTimeFormat without a fixed clock are a common output. They pass at 2pm local and fail at 6pm UTC because the day boundary changed.

Code works in isolation but fails in actual deployment contexts.
AI-assisted development analysis

Diagnose Claude Code tests pass local, fail by failure mode

Read the CI log carefully — the top failing assertion usually tells you which of the four categories you’re in.

CI errorRoot causeFix
expected '2026-04-15' but got '2026-04-16'Timezone differenceFix #1
process.env.X is undefinedEnv var missing in CIFix #2
Passes alone, fails in full suiteTest isolation / order dependencyFix #3
Module built with different Node versionNode / runtime version driftFix #4

Related errors we fix

Still stuck with Claude Code tests pass local, fail?

Emergency triage · $299 · 48h turnaround
We restore service and write the root-cause report.

If Claude Code’s tests are blocking your deploy:

  • CI has been red for >4 hours
  • You're about to ship and can't merge
  • Tests are flaky and you don't know which are real failures
  • Your team doesn't trust the CI output
start the triage →

Claude Code tests pass local, fail questions

Why do my Claude Code tests pass locally but fail in CI?+
Almost always one of four causes: implicit timezone or locale assumptions (your machine vs CI's UTC), environment variables set locally but missing in CI, test isolation problems (shared DB state or module singletons), or Node / Python version drift between your machine and the CI image. Fix each with a deterministic setup — fixed clock, documented env, isolated transactions, pinned runtime.
How do I make Claude Code write tests that pass in CI?+
Before asking Claude to write a test, make sure your repo has: a vitest or jest setup file that fixes TZ=UTC and uses fake timers, a .env.example with every required variable, a base test class that wraps each test in a DB transaction, and pinned Node / Python versions. With those guardrails in place, Claude's generated tests pass both locally and in CI on the first try.
Why does my test pass alone but fail in the full suite?+
Order dependency — an earlier test left state behind. Common culprits: shared database rows (wrap tests in transactions that roll back), module-level singletons (reset with vi.resetModules), file-system writes (use temp dirs), mock handlers leaking (restore after each test). Run tests in shuffled order with vitest --sequence.shuffle to surface these dependencies deliberately.
How do I handle timezone-sensitive tests in a Claude Code project?+
Set process.env.TZ = 'UTC' in your test setup file. Freeze the clock with vi.setSystemTime or jest.useFakeTimers. Replace new Date() in application code with an injected clock dependency. Never compare rendered date strings without first asserting the expected timezone. These four changes make timezone failures impossible.
What does it cost to fix a Claude Code CI setup?+
Integration Fix at $799 covers a full CI stabilisation — timezone setup, env vars documented, test isolation with DB transactions, Node pinning. Emergency Triage at $299 handles a single flaky test blocking a launch. Break-the-Fix-Loop at $3,999 is the right level if the whole suite is unreliable and you need a full test coverage pass plus CI re-architecture.
Should I skip CI and just deploy if Claude's tests keep failing?+
No. CI failure is the signal that something is wrong with your test environment — and industry benchmarks put AI-code vulnerability rates close to half (see our 2026 research), so the tests you're skipping are often the only thing catching them. Spend an afternoon fixing the CI setup once and you ship safely from then on. Skipping CI is how AI-built apps end up on the public-breach blotter.
Next step

Ship the fix. Keep the fix.

Emergency Triage restores service in 48 hours. Break the Fix Loop rebuilds CI so this error cannot ship again.

About the author

Hyder Shah leads Afterbuild Labs, shipping production rescues for apps built in Lovable, Bolt.new, Cursor, Replit, v0, and Base44. our rescue methodology.

Claude Code tests pass local, fail experts

If this problem keeps coming back, you probably need ongoing expertise in the underlying stack.

Sources