openclaw - ✅(Solved) Fix [Bug]: Compaction checkpoint .jsonl files double-count session cost in Usage dashboard [2 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#70686Fetched 2026-04-24 05:54:43
View on GitHub
Comments
0
Participants
1
Timeline
2
Reactions
0
Author
Participants
Timeline (top)
cross-referenced ×2

Compaction checkpoint files ({sessionId}.checkpoint.{checkpointId}.jsonl) are treated as independent session transcripts by the Usage cost calculator, causing each assistant turn to be counted twice (once from the primary session file, once from the checkpoint snapshot).

This inflates the displayed cost by approximately in the Usage dashboard, making it unreliable for cost tracking compared to the actual provider billing.

Root Cause

Both primary session files and checkpoint files reside in the same directory (~/.openclaw/agents/main/sessions/) and share the same .jsonl extension with full usage and cost metadata:

Primary file: 8534ecd0-...jsonl — contains messages with usage.cost Checkpoint: 8534ecd0-....checkpoint.b8d30d3b-....jsonl — same messages, same usage.cost

The function isUsageCountedSessionTranscriptFileName() in the artifacts module only excludes:

  • .deleted.* suffix
  • .reset.* suffix
  • .bak.* suffix

It does not exclude .checkpoint.{uuid}.jsonl files, so they pass the filter and get processed as independent sessions by loadCostUsageSummary().

Fix Action

Fixed

PR fix notes

PR #70749: fix(sessions): ignore compaction checkpoints in usage totals

Description (problem / solution / changelog)

Summary

  • Problem: compaction checkpoint transcripts (*.checkpoint.<uuid>.jsonl) were classified as primary session transcripts.
  • Why it matters: Usage aggregation scanned both the live transcript and checkpoint copy, inflating cost/token totals for sessions with compaction checkpoints.
  • What changed: checkpoint transcript filenames are now explicitly classified as checkpoint artifacts, excluded from primary/usage-counted transcript paths, and still removable by disk-budget cleanup.
  • What did NOT change (scope boundary): checkpoint creation, restore/branch behavior, reset/deleted archive semantics, and normal primary transcript accounting are unchanged.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #70686
  • This PR fixes a bug or regression

Root Cause (if applicable)

  • Root cause: isPrimarySessionTranscriptFileName() accepted every non-archive .jsonl, so generated compaction checkpoint snapshots were treated as independent sessions by usage scanning.
  • Missing detection / guardrail: artifact helper tests covered reset/deleted/bak archive suffixes but not checkpoint snapshot filenames, and usage aggregation had no duplicate-checkpoint fixture.
  • Contributing context (if known): checkpoint snapshots intentionally preserve the same usage-bearing messages as the primary transcript.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: src/config/sessions/artifacts.test.ts, src/infra/session-cost-usage.test.ts, src/config/sessions/disk-budget.test.ts
  • Scenario the test should lock in: checkpoint snapshots are recognized as checkpoint artifacts, not primary/usage-counted transcript files; duplicated usage in a checkpoint file is not included in daily totals; checkpoint artifacts remain removable under disk-budget pressure.
  • Why this is the smallest reliable guardrail: the bug is in filename classification used by the usage scanner; the seam test verifies the dashboard aggregation path; the cleanup test protects the shared helper’s disk-budget caller.
  • Existing test that already covers this (if any): none.
  • If no new test is added, why not: N/A.

User-visible / Behavior Changes

Usage totals no longer double-count assistant usage/cost data stored in compaction checkpoint snapshot files.

Diagram (if applicable)

Before:
primary.jsonl + primary.checkpoint.<uuid>.jsonl -> usage scan -> duplicated totals

After:
primary.jsonl + primary.checkpoint.<uuid>.jsonl -> usage scan -> primary totals only
                                         
Disk budget:
checkpoint snapshot -> checkpoint artifact queue -> removable under pressure

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No
  • If any Yes, explain risk + mitigation: N/A

Repro + Verification

Environment

  • OS: macOS local development
  • Runtime/container: Node 22+ repo toolchain via pnpm
  • Model/provider: N/A
  • Integration/channel (if any): Usage/session transcript aggregation
  • Relevant config (redacted): default session transcript layout

Steps

  1. Create sess-1.jsonl with an assistant message containing usage/cost data.
  2. Create sess-1.checkpoint.<uuid>.jsonl with the same usage/cost data.
  3. Run loadCostUsageSummary({ days: 30 }).

Expected

  • Totals include only the primary transcript usage.

Actual

  • Before this change, the checkpoint file was classified as primary and counted as a separate session transcript.

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

RED evidence:

  • pnpm test src/config/sessions/artifacts.test.ts failed before the usage fix because checkpoint filenames returned true for primary/usage-counted classification and parsed as a session id.
  • Self-review regression: pnpm test src/config/sessions/disk-budget.test.ts failed before the cleanup follow-up because checkpoint files became invisible to disk-budget cleanup.

Passing verification:

  • pnpm test src/config/sessions/artifacts.test.ts src/infra/session-cost-usage.test.ts src/config/sessions/disk-budget.test.ts
  • pnpm exec oxfmt --check src/config/sessions/artifacts.ts src/config/sessions/artifacts.test.ts src/config/sessions/disk-budget.ts src/config/sessions/disk-budget.test.ts src/infra/session-cost-usage.test.ts
  • pnpm check:changed
  • scripts/committer staged gates for both commits

Human Verification (required)

  • Verified scenarios: classifier excludes checkpoint snapshots from primary and usage-counted transcript paths; usage summary ignores duplicated usage in a checkpoint copy; disk-budget cleanup removes checkpoint artifacts before preserving active transcripts; reset/deleted archive counting remains covered.
  • Edge cases checked: touched files pass formatter check; changed-scope typecheck/lint/import-cycle guards/tests pass.
  • What you did not verify: full pnpm format:check for the entire repo, because unrelated pre-existing files currently fail formatting; this PR's touched files pass oxfmt --check.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No
  • If yes, exact upgrade steps: N/A

Risks and Mitigations

  • Risk: a user-created session id containing .checkpoint.<uuid> could be excluded from usage aggregation.
    • Mitigation: the pattern matches the exact generated compaction checkpoint filename shape; normal session ids and reset/deleted archives remain covered by tests.
  • Risk: checkpoint snapshots are storage artifacts and should not be counted, but they also should not become invisible to cleanup.
    • Mitigation: the PR adds explicit checkpoint artifact detection and disk-budget coverage.

Changed files

  • src/infra/session-cost-usage.test.ts (modified, +104/-0)
  • src/infra/session-cost-usage.ts (modified, +39/-1)

PR #70802: fix: exclude compaction checkpoint files from usage cost calculation

Description (problem / solution / changelog)

Closes #70686

Summary

The Usage dashboard reported roughly 2× the actual cost in sessions with compaction checkpoints enabled.

Root cause: Checkpoint files are named {sessionId}.checkpoint.{uuid}.jsonl and live in the same sessions/ directory as primary session files. They contain full snapshots of the session — including identical usage.cost records — but isPrimarySessionTranscriptFileName had no exclusion for the .checkpoint. suffix, so loadCostUsageSummary processed them as independent sessions and counted every assistant turn twice.

Fix: Add CHECKPOINT_SUFFIX_RE (/\.checkpoint\.[0-9a-f-]+\.jsonl$/i) to isPrimarySessionTranscriptFileName. Checkpoint files now return false from both isPrimarySessionTranscriptFileName and isUsageCountedSessionTranscriptFileName, matching the existing treatment of .deleted.* and .reset.* archive files.

Files changed

src/config/sessions/artifacts.ts

  • Add CHECKPOINT_SUFFIX_RE constant
  • Exit early in isPrimarySessionTranscriptFileName when the file name matches the checkpoint pattern

src/config/sessions/artifacts.test.ts

  • Add two new test cases: isPrimarySessionTranscriptFileName rejects checkpoint files
  • Add a dedicated test block: isUsageCountedSessionTranscriptFileName rejects checkpoint files

Test plan

  • pnpm test src/config/sessions/artifacts.test.ts — all cases pass including the new checkpoint assertions
  • Open Usage dashboard after a session with auto-compaction — total cost matches provider billing
  • Confirm non-checkpoint .jsonl files (primary sessions, reset/deleted archives) are still counted correctly

Changed files

  • src/config/sessions/artifacts.test.ts (modified, +17/-0)
  • src/config/sessions/artifacts.ts (modified, +5/-0)

Code Example

type=message, role=assistant → usage.cost.total = 0.0004893
type=message, role=assistant → usage.cost.total = 0.0005276

---

function isUsageCountedSessionTranscriptFileName(fileName) {
    if (fileName.includes(".checkpoint.")) return false;
    if (isPrimarySessionTranscriptFileName(fileName)) return true;
    return hasArchiveSuffix(fileName, "reset") || hasArchiveSuffix(fileName, "deleted");
}
RAW_BUFFERClick to expand / collapse

Bug type

Behavior bug (incorrect output/state without crash)

Summary

Compaction checkpoint files ({sessionId}.checkpoint.{checkpointId}.jsonl) are treated as independent session transcripts by the Usage cost calculator, causing each assistant turn to be counted twice (once from the primary session file, once from the checkpoint snapshot).

This inflates the displayed cost by approximately in the Usage dashboard, making it unreliable for cost tracking compared to the actual provider billing.

Steps to reproduce

  1. Use OpenClaw with auto-compaction enabled (default) for several days across multiple sessions
  2. Open the Control UI → Usage page → select a date range with checkpoints present
  3. Compare the header total cost against your actual provider billing (e.g. DeepSeek, OpenAI billing dashboard)
  4. Observe that OpenClaw reports roughly the actual cost

Root cause

Both primary session files and checkpoint files reside in the same directory (~/.openclaw/agents/main/sessions/) and share the same .jsonl extension with full usage and cost metadata:

Primary file: 8534ecd0-...jsonl — contains messages with usage.cost Checkpoint: 8534ecd0-....checkpoint.b8d30d3b-....jsonl — same messages, same usage.cost

The function isUsageCountedSessionTranscriptFileName() in the artifacts module only excludes:

  • .deleted.* suffix
  • .reset.* suffix
  • .bak.* suffix

It does not exclude .checkpoint.{uuid}.jsonl files, so they pass the filter and get processed as independent sessions by loadCostUsageSummary().

Evidence

Inspecting a checkpoint file confirms it contains full usage data:

type=message, role=assistant → usage.cost.total = 0.0004893
type=message, role=assistant → usage.cost.total = 0.0005276

These exact same cost.total values also appear in the primary session file, leading to double-counting.

Files involved

  • /app/dist/artifacts-_-1_LdEh.js — exports isUsageCountedSessionTranscriptFileName
  • /app/dist/session-cost-usage-DxrRtF-m.jsloadCostUsageSummary() consumes the filter

Related issues

  • #66057 — Reports the same checkpoint .jsonl files accumulating on disk indefinitely (8.4GB+ reported), but does not identify the cost double-counting impact
  • #42032 — Previous fix in the same function that added .reset.* and .deleted.* support to the usage counter (the checkpoint exclusion was missed)
  • #66013 — Proposal to truncate transcripts after durable compaction, which would reduce checkpoint accumulation

Expected behavior

Checkpoint files should be excluded from session cost aggregation since they are snapshots of the primary session, not independent sessions. The Usage dashboard total should match the sum of unique assistant turns across all sessions.

Alternatively, checkpoint files should be cleaned up automatically as proposed in #66057/#66013.

Actual behavior

Each assistant turn is counted as many times as there are checkpoint snapshots referencing it, inflating costs by ~2× or more.

Environment

  • OpenClaw version: 2026.4.21
  • OS: Linux (containerized, x64)
  • Install method: npm
  • Model: deepseek/deepseek-reasoner

Suggested fix

Add a checkpoint exclusion to isUsageCountedSessionTranscriptFileName():

function isUsageCountedSessionTranscriptFileName(fileName) {
    if (fileName.includes(".checkpoint.")) return false;
    if (isPrimarySessionTranscriptFileName(fileName)) return true;
    return hasArchiveSuffix(fileName, "reset") || hasArchiveSuffix(fileName, "deleted");
}

Additional context

Detected by cross-referencing OpenClaw Usage dashboard against DeepSeek billing CSV data. The discrepancy is invisible to users who do not compare against their provider's raw billing statements.

extent analysis

TL;DR

Update the isUsageCountedSessionTranscriptFileName function to exclude checkpoint files by adding a condition to check for the ".checkpoint." string in the file name.

Guidance

  • Modify the isUsageCountedSessionTranscriptFileName function as suggested in the issue to exclude checkpoint files from session cost aggregation.
  • Verify the fix by comparing the Usage dashboard total cost with the actual provider billing after the update.
  • Check the loadCostUsageSummary function to ensure it correctly consumes the updated filter and only processes primary session files.
  • Consider implementing the proposal to truncate transcripts after durable compaction to reduce checkpoint accumulation.

Example

function isUsageCountedSessionTranscriptFileName(fileName) {
    if (fileName.includes(".checkpoint.")) return false;
    if (isPrimarySessionTranscriptFileName(fileName)) return true;
    return hasArchiveSuffix(fileName, "reset") || hasArchiveSuffix(fileName, "deleted");
}

Notes

The suggested fix assumes that the isUsageCountedSessionTranscriptFileName function is the sole cause of the issue. However, other factors might contribute to the cost discrepancy, and additional debugging may be necessary.

Recommendation

Apply the suggested fix to update the isUsageCountedSessionTranscriptFileName function, as it directly addresses the identified root cause of the issue.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

Checkpoint files should be excluded from session cost aggregation since they are snapshots of the primary session, not independent sessions. The Usage dashboard total should match the sum of unique assistant turns across all sessions.

Alternatively, checkpoint files should be cleaned up automatically as proposed in #66057/#66013.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix [Bug]: Compaction checkpoint .jsonl files double-count session cost in Usage dashboard [2 pull requests, 1 participants]