thedotmack/claude-mem · error · Error

projection timeout must be a positive safe integer strictly…

Error message

projection timeout must be a positive safe integer strictly shorter than the Hub lease

What it means

fetchProjectionWithTimeout validates its timeoutMs argument (default PROJECTION_FETCH_TIMEOUT_MS) before starting the upstream projection fetch. The parameter must be a positive safe integer strictly below PROJECTION_LEASE_MS, because the timeout is what aborts the fetch and its body read within the Hub's fencing-lease window. The error fires only when a caller (e.g. drainProjection wiring, or a test passing a custom timeout) supplies a non-integer, zero/negative, non-safe-integer, or lease-exceeding timeout — an invalid input defect, not a runtime fetch failure.

Solutions

  1. Pass a positive safe integer timeoutMs that is strictly less than PROJECTION_LEASE_MS
  2. Omit the parameter to use the PROJECTION_FETCH_TIMEOUT_MS default, which is already validated at module load
  3. Clamp or validate caller-supplied timeout values before invoking fetchProjectionWithTimeout
  4. Fix test harnesses that inject timeout values exceeding the lease
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at workers/sync-hub/src/index.ts:558 when the library encounters an invalid state.

Common situations: See trigger scenarios.

Understand the failure class


AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17). Data as JSON: /api/errors/a9b563e95928e433. Report an issue: GitHub.

Appendix: source

Thrown at workers/sync-hub/src/index.ts:558

	status: number;
	bodyText: string;
}

export type ProjectionFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;

/**
 * Bound both the upstream fetch and consumption of its response body. A fetch
 * that returns headers but stalls the body must not outlive the fencing lease.
 */
export async function fetchProjectionWithTimeout(
	url: string,
	requestBody: string,
	secret: string,
	timeoutMs = PROJECTION_FETCH_TIMEOUT_MS,
	fetchImpl: ProjectionFetch = fetch,
): Promise<ProjectionFetchResult> {
	if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs >= PROJECTION_LEASE_MS) {
		throw new Error("projection timeout must be a positive safe integer strictly shorter than the Hub lease");
	}
	const controller = new AbortController();
	let timeout: ReturnType<typeof setTimeout> | undefined;
	const deadline = new Promise<never>((_resolve, reject) => {
		timeout = setTimeout(() => {
			controller.abort("projection fetch deadline exceeded");
			reject(new Error("projection_upstream_timeout"));
		}, timeoutMs);
	});
	const request = (async (): Promise<ProjectionFetchResult> => {
		const response = await fetchImpl(url, {
			method: "POST",
			headers: {
				"Content-Type": "application/json",
				Authorization: `Bearer ${secret}`,
			},
			body: requestBody,
			signal: controller.signal,

View on GitHub (pinned to d8bc9755e7)