Skip to content

Commit 9d2f138

Browse files
committed
fix(webapp): prevent OTLP nanosecond timestamp overflow (#3292)
Move BigInt() before multiplication to avoid IEEE 754 precision loss when converting epoch milliseconds to nanoseconds. Fixes 4 overflow sites: - getNowInNanoseconds(): BigInt(Date.now()) * 1_000_000n - calculateDurationFromStart(): BigInt(getTime()) * 1_000_000n - recordEvent() startTime: BigInt(getTime()) * 1_000_000n - runEngineHandlers OOM retry: BigInt(getTime()) * 1_000_000n Also adds epochMsToNanoseconds() helper to durations.ts for safe epoch-level conversions, and fixes seed script consistency.
1 parent be45cf9 commit 9d2f138

5 files changed

Lines changed: 10 additions & 5 deletions

File tree

apps/webapp/app/v3/eventRepository/common.server.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export function extractContextFromCarrier(carrier: Record<string, unknown>) {
2525
}
2626

2727
export function getNowInNanoseconds(): bigint {
28-
return BigInt(new Date().getTime() * 1_000_000);
28+
return BigInt(Date.now()) * 1_000_000n;
2929
}
3030

3131
export function getDateFromNanoseconds(nanoseconds: bigint): Date {
@@ -39,7 +39,7 @@ export function calculateDurationFromStart(
3939
) {
4040
const $endtime = typeof endTime === "string" ? new Date(endTime) : endTime;
4141

42-
const duration = Number(BigInt($endtime.getTime() * 1_000_000) - startTime);
42+
const duration = Number(BigInt($endtime.getTime()) * 1_000_000n - startTime);
4343

4444
if (minimumDuration && duration < minimumDuration) {
4545
return minimumDuration;

apps/webapp/app/v3/eventRepository/index.server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ async function recordRunEvent(
240240
runId: foundRun.friendlyId,
241241
...attributes,
242242
},
243-
startTime: BigInt((startTime?.getTime() ?? Date.now()) * 1_000_000),
243+
startTime: BigInt(startTime?.getTime() ?? Date.now()) * 1_000_000n,
244244
...optionsRest,
245245
});
246246

apps/webapp/app/v3/runEngineHandlers.server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,7 @@ export function registerRunEngineEventBusHandlers() {
555555
);
556556

557557
await eventRepository.recordEvent(retryMessage, {
558-
startTime: BigInt(time.getTime() * 1000000),
558+
startTime: BigInt(time.getTime()) * 1_000_000n,
559559
taskSlug: run.taskIdentifier,
560560
environment,
561561
attributes: {

apps/webapp/seed-ai-spans.mts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ function buildAiSpanTree(params: SpanTreeParams): CreateEventInput[] {
474474
attemptNumber?: number;
475475
}): CreateEventInput {
476476
const startNs = BigInt(opts.startMs) * BigInt(1_000_000);
477-
const durationNs = opts.durationMs * 1_000_000;
477+
const durationNs = BigInt(opts.durationMs) * BigInt(1_000_000);
478478
return {
479479
traceId,
480480
spanId: opts.spanId,

packages/core/src/v3/utils/durations.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ export function millisecondsToNanoseconds(milliseconds: number): number {
3232
return milliseconds * 1_000_000;
3333
}
3434

35+
/** Convert epoch milliseconds to nanoseconds using BigInt to avoid overflow. */
36+
export function epochMsToNanoseconds(ms: number): bigint {
37+
return BigInt(ms) * 1_000_000n;
38+
}
39+
3540
export function formatDurationNanoseconds(nanoseconds: number, options?: DurationOptions): string {
3641
return formatDurationMilliseconds(nanosecondsToMilliseconds(nanoseconds), options);
3742
}

0 commit comments

Comments
 (0)