Hmbown/CodeWhale · error · Error
publication timestamp is future or expired
Error message
publication timestamp is future or expired
What it means
activePublishingKey validates envelope timestamps: published_at may not be more than 5 minutes in the future, and not_after, when present, must still be in the future relative to `now`. A non-finite `now` also fails. This prevents publishing facts whose validity window is already expired or whose timestamps claim a future publish time.
Solutions
- Rebuild the envelope with a fresh published_at (and a not_after comfortably in the future) and re-sign it, then publish immediately
- Fix the local clock (NTP sync) if the machine's time is skewed and retry
- If using the `now` parameter in tests, pass a finite epoch-milliseconds number aligned with the envelope's timestamps
Example fix
// before
await activePublishingKey(staleEnvelope, keys); // not_after already passed
// after
const fresh = await signFacts(buildPayload({ published_at: new Date().toISOString(), not_after: futureDate }));
await activePublishingKey(fresh, keys); Defensive patterns
Strategy: validation
Validate before calling
const now = Date.now();
const pub = Date.parse(payload.published_at);
const exp = payload.not_after != null ? Date.parse(payload.not_after) : null;
if (!Number.isFinite(pub) || pub > now + 300_000) throw new Error("published_at is missing/invalid or too far in the future");
if (exp != null && exp <= now) throw new Error("not_after already expired — rebuild and re-sign the envelope"); Try / catch
try {
await activePublishingKey(envelope, keys);
} catch (err) {
if (err.message === "publication timestamp is future or expired") {
console.error("Rebuild the envelope with fresh timestamps (check clock skew via NTP) and re-sign before publishing");
process.exit(1);
}
throw err;
} Prevention
- Build and publish in one session so published_at/not_after stay within the validity window
- Keep machine clocks NTP-synced; check `date` against a reference if the error appears
- If passing an explicit `now`, ensure it is a finite epoch-milliseconds number consistent with the envelope
When it happens
Trigger: Calling activePublishingKey where utcTime(payload.published_at) > now + 300000, where payload.not_after <= now, or where the `now` argument is NaN/Infinity; also occurs when the system clock is wrong.
Common situations: The envelope was built earlier and its not_after expiry passed before the publish ran; the publishing machine's clock is skewed (drifted VM or wrong timezone handling); published_at was computed from a clock set minutes ahead.
Related errors
- cannot parse exactly one TypeScript TRUSTED_KEYS table
- lost precision: encode OTLP nanoseconds as a decimal string.
- GitHub timestamp is invalid
- Invalid failure observation time.
- Invalid failure observation time.
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/63041df5503d7ba7.
Report an issue: GitHub.
Appendix: source
Thrown at web/scripts/facts-publish.mjs:409
keys.push({ keyId, publicKey, status });
return "";
});
if (remainder.replace(/[\s,]/g, "")) throw new Error("unparsed TypeScript TRUSTED_KEYS entry");
return validateTrustedKeys(keys);
}
function loadTrustedKeysFromRepo() {
const keys = parseTsKeys(readBoundedFile(resolve(WEB_ROOT, "lib/cloud-facts/keys.ts"), 64 * 1024).toString("utf8"));
return new Map(keys.map((key) => [key.keyId, key]));
}
export function activePublishingKey(envelope, keys, now = Date.now()) {
const key = validateTrustedKeys(keys).find((key) => key.keyId === envelope.key_id && key.status === "active");
if (!key) throw new Error("primary signing key is not pinned and active; refusing publication");
const check = verifyEnvelope(envelope, key.publicKey);
if (!check.ok) throw new Error(`envelope does not verify: ${check.errors.join("; ")}`);
if (!Number.isFinite(now) || utcTime(check.payload.published_at) > now + 300_000 ||
(check.payload.not_after != null && utcTime(check.payload.not_after) <= now)) throw new Error("publication timestamp is future or expired");
return { key, check };
}
function refuseUnderCi() {
for (const marker of CI_MARKERS) {
if (process.env[marker] && !/^(0|false|no|off)$/i.test(process.env[marker])) {
throw new Error(`refusing to run with a secret under CI (${marker} is set); publish from the founder's machine`);
}
}
}
function sqlLiteral(value) {
if (value === null || value === undefined) return "null";
return `'${String(value).replace(/'/g, "''")}'`;
}
export function emitSql(envelope, { publishedBy = "", publicKeyB64, notes = "" }) {
if (!publicKeyB64) throw new Error("public key required to emit the facts_key row");View on GitHub (pinned to 433685b202)