Skip to content

Commit 479b708

Browse files
fix: handle 416 explicitly in s2s read session (#99)
1 parent 21529b9 commit 479b708

File tree

4 files changed

+48
-8
lines changed

4 files changed

+48
-8
lines changed

packages/streamstore/src/error.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -356,26 +356,35 @@ export class FencingTokenMismatchError extends S2Error {
356356
* This occurs when you specify a `startSeqNum` that is greater than the current tail
357357
* of the stream (HTTP 416 Range Not Satisfiable).
358358
*
359+
* The `tail` property contains the current tail position of the stream.
360+
*
359361
* To handle this gracefully, you can set `clamp: true` in your read options to
360362
* automatically start from the tail instead of throwing an error.
361363
*/
362364
export class RangeNotSatisfiableError extends S2Error {
365+
/** The current tail position of the stream. */
366+
public readonly tail?: { seq_num: number; timestamp: number };
367+
363368
constructor({
364-
message = "Range not satisfiable: requested position is beyond the stream tail. Use 'clamp: true' to start from the tail instead.",
365369
code,
366370
status = 416,
371+
tail,
367372
}: {
368-
message?: string;
369373
code?: string;
370374
status?: number;
375+
tail?: { seq_num: number; timestamp: number };
371376
} = {}) {
377+
const message = tail
378+
? `Range not satisfiable: requested position is beyond the stream tail (seq_num=${tail.seq_num}). Use 'clamp: true' to start from the tail instead.`
379+
: "Range not satisfiable: requested position is beyond the stream tail. Use 'clamp: true' to start from the tail instead.";
372380
super({
373381
message,
374382
code,
375383
status,
376384
origin: "server",
377385
});
378386
this.name = "RangeNotSatisfiableError";
387+
this.tail = tail;
379388
}
380389
}
381390

packages/streamstore/src/lib/stream/transport/fetch/shared.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,15 @@ export async function streamRead<Format extends "string" | "bytes" = "string">(
5353
if (response.error) {
5454
const status = response.response.status;
5555
if (status === 416) {
56-
throw new RangeNotSatisfiableError({ status });
56+
const err = response.error as {
57+
tail?: { seq_num: number; timestamp: number };
58+
code?: string;
59+
};
60+
throw new RangeNotSatisfiableError({
61+
status,
62+
tail: err.tail,
63+
code: err.code,
64+
});
5765
}
5866
throw makeServerError(
5967
{ status, statusText: response.response.statusText },

packages/streamstore/src/lib/stream/transport/s2s/index.ts

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -404,23 +404,40 @@ class S2SReadSession<Format extends "string" | "bytes" = "string">
404404

405405
stream.on("data", (chunk: Buffer) => {
406406
try {
407-
if ((responseCode ?? 500) >= 400) {
407+
const status = responseCode ?? 500;
408+
if (status >= 400) {
408409
const errorText = textDecoder.decode(chunk);
410+
debug("error response: status=%d body=%s", status, errorText);
411+
if (status === 416) {
412+
try {
413+
const errorJson = JSON.parse(errorText);
414+
safeError(
415+
new RangeNotSatisfiableError({
416+
status,
417+
code: errorJson.code,
418+
tail: errorJson.tail,
419+
}),
420+
);
421+
} catch {
422+
safeError(new RangeNotSatisfiableError({ status }));
423+
}
424+
return;
425+
}
409426
try {
410427
const errorJson = JSON.parse(errorText);
411428
safeError(
412429
new S2Error({
413430
message: errorJson.message ?? "Unknown error",
414431
code: errorJson.code,
415-
status: responseCode,
432+
status,
416433
origin: "server",
417434
}),
418435
);
419436
} catch {
420437
safeError(
421438
new S2Error({
422439
message: errorText || "Unknown error",
423-
status: responseCode,
440+
status,
424441
origin: "server",
425442
}),
426443
);
@@ -441,7 +458,13 @@ class S2SReadSession<Format extends "string" | "bytes" = "string">
441458

442459
// Map known read errors
443460
if (status === 416) {
444-
safeError(new RangeNotSatisfiableError({ status }));
461+
safeError(
462+
new RangeNotSatisfiableError({
463+
status,
464+
code: errorJson.code,
465+
tail: errorJson.tail,
466+
}),
467+
);
445468
} else {
446469
safeError(
447470
makeServerError(

s2-protos

0 commit comments

Comments
 (0)