Skip to content

fix: skip expired snapshots and fallback to fresh sandbox#412

Merged
sweetmantech merged 6 commits intotestfrom
fix/snapshot-expiry-fallback
Apr 7, 2026
Merged

fix: skip expired snapshots and fallback to fresh sandbox#412
sweetmantech merged 6 commits intotestfrom
fix/snapshot-expiry-fallback

Conversation

@sweetmantech
Copy link
Copy Markdown
Contributor

@sweetmantech sweetmantech commented Apr 7, 2026

Summary

  • Checks expires_at before attempting snapshot-based sandbox creation
  • Falls back to a fresh sandbox if snapshot creation fails for any reason
  • Applies to both processCreateSandbox (POST /api/sandboxes handler) and createSandboxFromSnapshot (shared domain logic)

Context

Accounts with expired Vercel snapshots (sidney@recoupable.com, sidney+1@recoupable.com) caused Sandbox.create() to throw "Status code 400 is not ok" with no recovery path, returning a 400 error to the client.

Test plan

  • Verify sandbox creation works for accounts with expired snapshots
  • Verify sandbox creation still works for accounts with valid snapshots
  • Verify sandbox creation works for accounts with no snapshots

🤖 Generated with Claude Code


Summary by cubic

Skip expired Vercel snapshots and fall back to a fresh sandbox when restore fails, routing all sandbox creation through one path. Prevents 400s and keeps POST /api/sandboxes reliable.

  • Bug Fixes

    • Treat snapshots with expires_at in the past, equal to now, or invalid as expired.
    • On snapshot restore failure, create a fresh sandbox to keep requests successful.
  • Refactors

    • Added getValidSnapshotId and createSandboxWithFallback with error logging.
    • Centralized flow: processCreateSandbox -> createSandboxFromSnapshot -> createSandboxWithFallback; DB insert uses sandbox.sandboxId.

Written for commit 936b048. Summary will update on new commits.

Summary by CodeRabbit

  • Bug Fixes
    • Enhanced sandbox creation with automatic fallback mechanism when snapshot-based sandbox creation fails or encounters expired snapshots.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 7, 2026

Warning

Rate limit exceeded

@sweetmantech has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 7 minutes and 4 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 7 minutes and 4 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9de548c8-4aa2-454c-8965-d1622921f7b6

📥 Commits

Reviewing files that changed from the base of the PR and between c48d9f8 and 936b048.

⛔ Files ignored due to path filters (5)
  • lib/sandbox/__tests__/createSandboxFromSnapshot.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/sandbox/__tests__/createSandboxPostHandler.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/sandbox/__tests__/createSandboxWithFallback.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/sandbox/__tests__/getValidSnapshotId.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/sandbox/__tests__/processCreateSandbox.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
📒 Files selected for processing (4)
  • lib/sandbox/createSandboxFromSnapshot.ts
  • lib/sandbox/createSandboxWithFallback.ts
  • lib/sandbox/getValidSnapshotId.ts
  • lib/sandbox/processCreateSandbox.ts
📝 Walkthrough

Walkthrough

This PR refactors sandbox creation functions to introduce centralized snapshot validation via a new getValidSnapshotId utility that checks snapshot existence and expiration. Both creation paths now use this utility and include error handling to fall back to fresh sandbox creation when snapshot-based creation fails.

Changes

Cohort / File(s) Summary
Snapshot Validation Utility
lib/sandbox/getValidSnapshotId.ts
New function that fetches and validates snapshots for an account, checking both existence and expiration (expires_at before current time), returning the snapshot ID only if valid.
Refactored Sandbox Creation
lib/sandbox/createSandboxFromSnapshot.ts, lib/sandbox/processCreateSandbox.ts
Both files now use the centralized getValidSnapshotId() for snapshot lookup. Added try/catch blocks to handle creation failures with automatic fallback to fresh sandbox creation (createSandbox({})). Added createSandboxWithFallback helper in processCreateSandbox. The fromSnapshot flag now reflects successful snapshot-based creation rather than just snapshot existence.

Sequence Diagram

sequenceDiagram
    participant Caller
    participant createSandboxFromSnapshot
    participant getValidSnapshotId
    participant Database
    participant VercelAPI as Vercel API
    
    Caller->>createSandboxFromSnapshot: createSandboxFromSnapshot(accountId)
    createSandboxFromSnapshot->>getValidSnapshotId: getValidSnapshotId(accountId)
    getValidSnapshotId->>Database: selectAccountSnapshots(accountId)
    Database-->>getValidSnapshotId: snapshots[]
    Note over getValidSnapshotId: Validate snapshot<br/>expiration
    getValidSnapshotId-->>createSandboxFromSnapshot: snapshotId or undefined
    
    alt snapshotId exists
        createSandboxFromSnapshot->>VercelAPI: createSandbox({source: {type: "snapshot", snapshotId}})
        alt Success
            VercelAPI-->>createSandboxFromSnapshot: sandbox
            Note over createSandboxFromSnapshot: fromSnapshot = true
        else Failure (expired/invalid)
            VercelAPI-->>createSandboxFromSnapshot: Error
            createSandboxFromSnapshot->>VercelAPI: createSandbox({})
            VercelAPI-->>createSandboxFromSnapshot: fresh sandbox
            Note over createSandboxFromSnapshot: fromSnapshot = false<br/>(fallback)
        end
    else No valid snapshot
        createSandboxFromSnapshot->>VercelAPI: createSandbox({})
        VercelAPI-->>createSandboxFromSnapshot: fresh sandbox
        Note over createSandboxFromSnapshot: fromSnapshot = false
    end
    
    createSandboxFromSnapshot->>Database: Insert/update sandbox record
    createSandboxFromSnapshot-->>Caller: {sandbox, fromSnapshot}
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

📸 Snapshots checked, their expiry weighed,
Fallbacks ready if they fade,
Fresh sandboxes bloom from ashes past,
Resilient creation built to last! 🎯✨

🚥 Pre-merge checks | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Solid & Clean Code ⚠️ Warning The date comparison logic in getValidSnapshotId.ts line 14 uses fragile Date object comparison that fails to handle edge cases: exact expiry moments treated as valid and Invalid Date objects treated as valid. Replace line 14 with numeric timestamp comparison: const expiresAt = new Date(snapshot.expires_at).getTime(); if (Number.isNaN(expiresAt) || expiresAt <= Date.now()) to explicitly validate dates and use proper comparison.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/snapshot-expiry-fallback

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Apr 7, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
recoup-api Ready Ready Preview Apr 7, 2026 5:56pm

Request Review

Copy link
Copy Markdown
Contributor Author

@sweetmantech sweetmantech left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing unit tests for tdd test driven development.

Comment on lines +24 to +44
const isExpired = snapshot?.expires_at && new Date(snapshot.expires_at) < new Date();
const snapshotId = !isExpired ? snapshot?.snapshot_id : undefined;

let sandbox: Sandbox;
let fromSnapshot = false;

if (snapshotId) {
try {
const result = await createSandbox({
source: { type: "snapshot", snapshotId },
});
sandbox = result.sandbox;
fromSnapshot = true;
} catch {
const result = await createSandbox({});
sandbox = result.sandbox;
}
} else {
const result = await createSandbox({});
sandbox = result.sandbox;
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SRP

  • new lib for snapshotID logic

* @param accountId - The account to look up
* @returns The snapshot ID if it exists and has not expired
*/
async function getValidSnapshotId(accountId: string): Promise<string | undefined> {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SRP - new lib file for getValidSnapshotId

Comment on lines +46 to +59
if (snapshotId) {
try {
const createResult = await createSandbox({
source: { type: "snapshot", snapshotId },
});
result = createResult.response;
} catch {
const freshResult = await createSandbox({});
result = freshResult.response;
}
} else {
const freshResult = await createSandbox({});
result = freshResult.response;
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DRY

  • actual: createSandbox({}); defined twice.
  • required: clean logic so createSandbox({}); is only called once.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 2 files

Confidence score: 3/5

  • There is some merge risk because lib/sandbox/createSandboxFromSnapshot.ts introduces new expired-snapshot and try/catch fallback behavior without test coverage, which increases regression risk in sandbox creation flows.
  • The most severe issue is the untested logic around expires_at handling and fallback behavior in lib/sandbox/createSandboxFromSnapshot.ts; if it behaves unexpectedly, users may get incorrect sandbox restore behavior.
  • Both lib/sandbox/createSandboxFromSnapshot.ts and lib/sandbox/processCreateSandbox.ts currently swallow snapshot creation exceptions without logging, which reduces production observability and can slow incident diagnosis.
  • Pay close attention to lib/sandbox/createSandboxFromSnapshot.ts, lib/sandbox/processCreateSandbox.ts - missing tests and missing error logs around fallback paths can hide failures.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="lib/sandbox/createSandboxFromSnapshot.ts">

<violation number="1" location="lib/sandbox/createSandboxFromSnapshot.ts:24">
P1: Custom agent: **Flag AI Slop and Fabricated Changes**

Neither of the two new behaviors introduced by this bug fix is covered by tests: (1) expired-snapshot skipping (`expires_at` check) and (2) the try/catch fallback to a fresh sandbox. The test file already exists and the assertions are straightforward to add — e.g., a snapshot with a past `expires_at` should call `createSandbox({})`, and a rejecting `mockCreateSandbox` on a valid snapshot should still yield a fresh sandbox. Without these, the exact regression this PR fixes can silently reappear.</violation>

<violation number="2" location="lib/sandbox/createSandboxFromSnapshot.ts:37">
P2: The `catch` block silently swallows the snapshot creation error. Log the error before falling back so failures are observable in production.</violation>
</file>

<file name="lib/sandbox/processCreateSandbox.ts">

<violation number="1" location="lib/sandbox/processCreateSandbox.ts:52">
P2: Log the error before falling back to a fresh sandbox. Silently swallowing the exception removes any signal about *why* snapshot creation failed, making production incidents harder to diagnose.</violation>
</file>
Architecture diagram
sequenceDiagram
    participant Client
    participant Service as Sandbox Domain Service
    participant DB as Database
    participant Provider as External Sandbox API
    
    Note over Service,Provider: Includes processCreateSandbox & createSandboxFromSnapshot
    
    Client->>Service: Request Sandbox Creation
    Service->>DB: selectAccountSnapshots(accountId)
    DB-->>Service: Snapshot list (with expires_at)
    
    Service->>Service: CHANGED: Check expiration status
    
    alt Valid Snapshot exists
        Service->>Provider: createSandbox(snapshotId)
        alt Success
            Provider-->>Service: Sandbox instance
        else NEW: Snapshot Restore Fails (e.g. 400 Error)
            Service->>Service: Catch error
            Service->>Provider: NEW: createSandbox() (Fallback to Fresh)
            Provider-->>Service: Sandbox instance
        end
    else NEW: Snapshot expired or none found
        Service->>Provider: createSandbox() (Fresh)
        Provider-->>Service: Sandbox instance
    end
    
    Service->>DB: insertAccountSandbox(accountId, sandboxId)
    DB-->>Service: Saved
    Service-->>Client: Sandbox Response (runId, sandboxId)
Loading

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

* @param snapshotId - Optional snapshot ID to restore from
* @returns The sandbox creation response
*/
async function createSandboxWithFallback(snapshotId: string | undefined) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SRP

  • new lib file for createSandboxWithFallback

Comment on lines +26 to +40
if (snapshotId) {
try {
sandbox = (await createSandbox({ source: { type: "snapshot", snapshotId } })).sandbox;
fromSnapshot = true;
} catch (error) {
console.error(
"Snapshot sandbox creation failed, falling back to fresh sandbox:",
error,
);
}
}

if (!fromSnapshot) {
sandbox = (await createSandbox({})).sandbox;
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DRY - use the shared lib/sandbox/createSandboxWithFallback.ts

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 4 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="lib/sandbox/createSandboxWithFallback.ts">

<violation number="1" location="lib/sandbox/createSandboxWithFallback.ts:10">
P1: Custom agent: **Flag AI Slop and Fabricated Changes**

Bug-fix PR with no automated tests for the new fallback behavior. The two code paths (snapshot restore succeeds vs. fails-and-falls-back) are straightforward to test by mocking `createSandbox` to throw on the snapshot call. A regression test here would prevent this exact class of bug from silently reappearing.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 5 files (changes from recent commits).

Requires human review: Auto-approval blocked by 1 unresolved issue from previous reviews.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
lib/sandbox/processCreateSandbox.ts (1)

37-70: Trim processCreateSandbox back to the core flow.

It still handles snapshot lookup, sandbox creation, persistence, and prompt dispatch in one 34-line function. Extracting the prompt block into a small helper would keep the primary path under the repo’s 20-line guideline and isolate the non-fatal error path for tests.

As per coding guidelines, "Flag functions longer than 20 lines" and "Keep functions small and focused".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/sandbox/processCreateSandbox.ts` around lines 37 - 70, The function
processCreateSandbox is too long and should delegate the optional prompt
dispatch to a small helper; extract the prompt handling block (the if (prompt)
try/catch that calls triggerPromptSandbox and sets runId) into a new async
helper function (e.g., dispatchPromptIfPresent or triggerPromptForSandbox) that
accepts { prompt, sandboxId, accountId } and returns the runId or undefined,
preserve the try/catch there and log errors, then call that helper from
processCreateSandbox and merge its returned runId into the result so the main
function stays focused on snapshot lookup, createSandboxWithFallback, and
insertAccountSandbox and reduces below the 20-line guideline.
lib/sandbox/createSandboxFromSnapshot.ts (1)

27-32: Emit a warning before falling back.

The fallback is the right behavior, but this catch makes repeated snapshot-restore failures invisible. A warn/metric with accountId and snapshotId would let you spot widespread expiry or restore regressions without failing the request.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/sandbox/createSandboxFromSnapshot.ts` around lines 27 - 32, The catch
block swallowing snapshot restore errors should emit a warning with context
before falling back; modify the catch in createSandboxFromSnapshot (around the
createSandbox({ source: { type: "snapshot", snapshotId } }) call) to log a
warning including snapshotId and accountId (and the caught error message/stack)
via the existing logger/processLogger so repeated snapshot-restore failures are
visible and can be instrumented/metricized; keep the fallback behavior but
ensure the warning includes clear context (e.g., "snapshot restore failed,
falling back to fresh") plus snapshotId and accountId.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/sandbox/getValidSnapshotId.ts`:
- Around line 14-15: In getValidSnapshotId, treat snapshots with expires_at
equal to now or with unparsable timestamps as invalid by parsing
snapshot.expires_at once into a Date (or timestamp), checking for NaN (e.g.,
isNaN(date.getTime()) or Number.isNaN(parsed)), and using a <= comparison
against new Date() (or Date.now()) instead of <; when the parsed date is NaN or
parsed <= now, return undefined so the restore path never receives expired or
invalid snapshots.

---

Nitpick comments:
In `@lib/sandbox/createSandboxFromSnapshot.ts`:
- Around line 27-32: The catch block swallowing snapshot restore errors should
emit a warning with context before falling back; modify the catch in
createSandboxFromSnapshot (around the createSandbox({ source: { type:
"snapshot", snapshotId } }) call) to log a warning including snapshotId and
accountId (and the caught error message/stack) via the existing
logger/processLogger so repeated snapshot-restore failures are visible and can
be instrumented/metricized; keep the fallback behavior but ensure the warning
includes clear context (e.g., "snapshot restore failed, falling back to fresh")
plus snapshotId and accountId.

In `@lib/sandbox/processCreateSandbox.ts`:
- Around line 37-70: The function processCreateSandbox is too long and should
delegate the optional prompt dispatch to a small helper; extract the prompt
handling block (the if (prompt) try/catch that calls triggerPromptSandbox and
sets runId) into a new async helper function (e.g., dispatchPromptIfPresent or
triggerPromptForSandbox) that accepts { prompt, sandboxId, accountId } and
returns the runId or undefined, preserve the try/catch there and log errors,
then call that helper from processCreateSandbox and merge its returned runId
into the result so the main function stays focused on snapshot lookup,
createSandboxWithFallback, and insertAccountSandbox and reduces below the
20-line guideline.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6b5df283-b2a4-4da6-8678-d874ddbb93d9

📥 Commits

Reviewing files that changed from the base of the PR and between f010076 and c48d9f8.

⛔ Files ignored due to path filters (3)
  • lib/sandbox/__tests__/createSandboxFromSnapshot.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/sandbox/__tests__/createSandboxPostHandler.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/sandbox/__tests__/getValidSnapshotId.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
📒 Files selected for processing (3)
  • lib/sandbox/createSandboxFromSnapshot.ts
  • lib/sandbox/getValidSnapshotId.ts
  • lib/sandbox/processCreateSandbox.ts

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 3 files (changes from recent commits).

Requires human review: Auto-approval blocked by 1 unresolved issue from previous reviews.

@sweetmantech
Copy link
Copy Markdown
Contributor Author

Preview Deployment Test Results

All three test cases pass on the preview deployment:

Test Account ID Result Sandbox ID
Sidney account 1 1ca89eeb-... ✅ success sbx_DTbCUoQU8ytEyrKPYYQZJgR5qgNW
Sidney account 2 848cd58d-... ✅ success sbx_NXipp0s9eCZyN8Jg3R3mHHI1eW2a
No account_id (default) ✅ success sbx_RGPrPD413TC1wuJ6MMGrmPjsAuof

Previously these accounts returned "Status code 400 is not ok" on prod due to expired snapshots with no fallback.

sweetmantech and others added 6 commits April 7, 2026 12:55
Accounts with expired Vercel snapshots caused Sandbox.create() to throw
"Status code 400 is not ok" with no recovery path.

Changes:
- Extract getValidSnapshotId to its own lib file (SRP)
- Both processCreateSandbox and createSandboxFromSnapshot use it
- Add fallback: if snapshot creation fails, create a fresh sandbox
- DRY: processCreateSandbox uses createSandboxWithFallback helper
  so createSandbox({}) is only called once
- Add tests for getValidSnapshotId (5 tests)
- Add snapshot expiry and fallback tests to existing test files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Single createSandbox({}) call path instead of duplicating it in
both the catch block and the else branch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…r logging

- Extract createSandboxWithFallback to lib/sandbox/createSandboxWithFallback.ts (SRP)
- processCreateSandbox and createSandboxFromSnapshot use it / follow same pattern
- Log snapshot creation errors before falling back to fresh sandbox
- Add 4 unit tests for createSandboxWithFallback

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…allback

- createSandboxFromSnapshot now delegates to createSandboxWithFallback
  instead of duplicating snapshot/fresh fallback logic
- createSandboxWithFallback returns full SandboxCreateResult + fromSnapshot
- Update tests to match new delegation pattern

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…apshot

processCreateSandbox now delegates sandbox creation to
createSandboxFromSnapshot instead of calling getValidSnapshotId,
createSandboxWithFallback, and insertAccountSandbox directly.

This eliminates the duplicated snapshot lookup + fallback + DB insert
logic between the two functions. processCreateSandbox adds only
the prompt-triggering behavior on top.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use <= instead of < so snapshots expiring exactly now are treated as
expired. Add NaN check so unparsable expires_at values don't slip
through as valid.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@sweetmantech sweetmantech force-pushed the fix/snapshot-expiry-fallback branch from bb1a73a to 936b048 Compare April 7, 2026 17:55
@sweetmantech sweetmantech merged commit a3eec5d into test Apr 7, 2026
4 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant