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
- 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'.
- 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).
- Update the package if Anthropic changed the timestamp format — the parser may need to accept the new format.
- 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
- Keep fixtures/mocks realistic: expires_at must be ISO-8601 (e.g. 2026-08-30T12:00:00Z), never "never" or "".
- When upgrading Anthropic API versions, diff the expires_at format against what your parser accepts.
- Prefer omitting expires_at (null/undefined is valid) over fabricating placeholder strings in tests.
- Sanity-check parsed timestamps downstream (positive, in the future) to catch format drift early.
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
- Anthropic Files API credential is required
- Cannot delete an invalid Anthropic file handle
- invalid date: {value}
- date is out of range
- dates before 1970 are unsupported
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/0ed420c1eff114fd.
Report an issue: GitHub.