can1357/oh-my-pi · error

Anthropic file upload returned an invalid expiration time

Error message

Anthropic file upload returned an invalid expiration time

What it means

After a successful upload, the client converts the `expires_at` string from the Anthropic Files API metadata into a numeric epoch-milliseconds timestamp via `Date.parse`. If the string is present but cannot be parsed into a finite date (unparseable, empty-but-nonnull, or malformed format), the client throws instead of returning a bogus expiry that would break downstream TTL/retention logic.

Source

Thrown at packages/coding-agent/src/blob-broker/provider-files-anthropic.ts:79

		typeof record.size_bytes !== "number" ||
		!Number.isSafeInteger(record.size_bytes) ||
		record.size_bytes < 0 ||
		!(record.expires_at === undefined || record.expires_at === null || typeof record.expires_at === "string")
	) {
		throw new Error("Anthropic file upload returned invalid metadata");
	}
	return {
		id: record.id,
		mime_type: record.mime_type,
		size_bytes: record.size_bytes,
		expires_at: record.expires_at,
	};
}

function parseExpiresAt(value: string | null | undefined): number | undefined {
	if (value == null) return undefined;
	const expiresAt = Date.parse(value);
	if (!Number.isFinite(expiresAt)) throw new Error("Anthropic file upload returned an invalid expiration time");
	return expiresAt;
}

function uploadedFile(request: ProviderFileUploadRequest): File {
	const preferred = request.filename?.trim().replaceAll("\\", "/").split("/").pop();
	const filename = preferred && preferred !== "." && preferred !== ".." ? preferred : "upload";
	return new File([request.bytes], filename, { type: request.mimeType });
}

/**
 * Create a native Anthropic Files API client for an official Anthropic Messages model.
 * Unsupported providers, APIs, and non-Anthropic endpoints return `null` without making a request.
 */
export function createAnthropicFileClient(
	model: Model,
	credential: string,
	fetchImpl: FetchImpl = globalThis.fetch,
): ProviderFileClient | null {

View on GitHub (pinned to 9690622007)

Solutions

  1. Log the exact `expires_at` value from the upload response and verify it is an ISO-8601 date-time string (e.g. 2026-08-30T12:00:00Z) that `new Date(value).toString()` does not return 'Invalid Date'.
  2. Fix any mock or recorded fixture so expires_at is a real ISO-8601 string, or omit the field entirely (null/undefined is treated as no expiry, which is valid).
  3. Update the package if Anthropic changed the timestamp format — the parser may need to accept the new format.
  4. Check that no middleware or proxy is corrupting the response body between api.anthropic.com and your process.
Defensive patterns

Strategy: validation

Validate before calling

// check expiry strings from the API before passing them on
function isParseableDate(v: string | null | undefined): boolean {
  if (v == null) return true;
  return Number.isFinite(Date.parse(v));
}

Type guard

const hasValidExpiry = (m: { expires_at?: string | null }): boolean => m.expires_at == null || Number.isFinite(Date.parse(m.expires_at));

Try / catch

try {
  const handle = await anthropicFiles.upload(request);
} catch (err) {
  if (err instanceof Error && err.message.includes("invalid expiration time")) {
    logger.error("Anthropic expires_at unparseable — API format may have changed", { cause: err });
    // treat as unknown expiry or re-upload
  } else throw err;
}

Prevention

When it happens

Trigger: `parseExpiresAt` is called during `upload` with a `metadata.expires_at` value that is a non-null string but not a parseable RFC/date-time string — e.g. `""`, `"null"`, `"undefined"`, `"2026-13-45T99:00:00Z"`, or a locale-formatted date.

Common situations: Anthropic changes the expires_at wire format (e.g. from ISO-8601 to epoch seconds as a string); a mock/fixture records expires_at as something like "never" or an empty string; a proxy mangles the JSON; an environment with a patched Date.parse or exotic locale assumptions misinterprets an otherwise valid date string.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/0ed420c1eff114fd. Report an issue: GitHub.