Setup: plugin 1.0.0, Claude Code v2.1.216 (macOS), langfuse-sdk 4.14.1, Langfuse Cloud EU (server 3.222.0).
Observed: when a Claude Code session is resumed/continued (which copies the conversation history into a new transcript JSONL with a new session id), the hook re-ingests the copied history as a brand-new trace. The per-file byte offset starts at 0 for the new file, so every already-exported turn is exported again.
Evidence (from our project): two traces whose first 12 "LLM Call" generations are byte-identical — same start timestamps to the millisecond, same usage on every call (e.g. call 12 in both: input 2, output 540, cache_read 56,889, cache_creation 1,520 → $0.102909) — but different observation IDs and different session_id/transcript_path metadata. The two traces diverge from call 13 onward, exactly where the fork happened.
Impact:
- Cost/token aggregates are inflated by the duplicated span of history (~$1.27 in our one observed case; scales with how late in a session the resume happens).
- Free-tier units are consumed twice for the same work.
Suggestion: the hook already derives deterministic W3C trace ids per turn (CC_LANGFUSE_TRACE_SEED path). If the id derivation were stable across transcript copies (e.g. seeded from the turn's original timestamp + content hash rather than the containing file/session id), re-ingested history would upsert onto the existing traces instead of creating new ones. Alternatively: skip turns whose message timestamps predate the new session file's creation.
Happy to provide more trace-level detail from our project if useful.
Setup: plugin 1.0.0, Claude Code v2.1.216 (macOS), langfuse-sdk 4.14.1, Langfuse Cloud EU (server 3.222.0).
Observed: when a Claude Code session is resumed/continued (which copies the conversation history into a new transcript JSONL with a new session id), the hook re-ingests the copied history as a brand-new trace. The per-file byte offset starts at 0 for the new file, so every already-exported turn is exported again.
Evidence (from our project): two traces whose first 12 "LLM Call" generations are byte-identical — same start timestamps to the millisecond, same usage on every call (e.g. call 12 in both: input 2, output 540, cache_read 56,889, cache_creation 1,520 → $0.102909) — but different observation IDs and different
session_id/transcript_pathmetadata. The two traces diverge from call 13 onward, exactly where the fork happened.Impact:
Suggestion: the hook already derives deterministic W3C trace ids per turn (
CC_LANGFUSE_TRACE_SEEDpath). If the id derivation were stable across transcript copies (e.g. seeded from the turn's original timestamp + content hash rather than the containing file/session id), re-ingested history would upsert onto the existing traces instead of creating new ones. Alternatively: skip turns whose message timestamps predate the new session file's creation.Happy to provide more trace-level detail from our project if useful.