{"record":{"id":"024640a03cc15046","repo":"different-ai/openwork","slug":"mcp-oauth-persistence-invalid","errorCode":"MCP_OAUTH_PERSISTENCE_INVALID","errorMessage":"The OAuth persistence adapter returned an invalid ${field}.","messagePattern":"The OAuth persistence adapter returned an invalid (.+?)\\.","errorType":"error_code","errorClass":"EnterpriseMcpOAuthContractError","httpStatus":null,"severity":"error","filePath":"packages/enterprise-mcp-client/src/oauth-provider.ts","lineNumber":42,"sourceCode":"import { isAuthorizationServerDiscoveryBound } from \"./oauth-discovery-binding.js\"\n\ntype OAuthFlowContext =\n  | { kind: \"connect\"; authorizationId?: string }\n  | { kind: \"callback\"; authorizationId: string }\n  | { kind: \"runtime\" }\n\ntype VerifiedOAuthDiscoveryState = OAuthDiscoveryState & {\n  openworkMetadataVerification?: {\n    version: 1\n    issuer: string\n  }\n}\n\nconst oauthClientInformationMixedSchema = OAuthClientInformationFullSchema.or(OAuthClientInformationSchema)\n\nfunction assertFiniteEpoch(value: number, field: string): number {\n  if (!Number.isFinite(value) || value < 0) {\n    throw new EnterpriseMcpOAuthContractError(\n      \"MCP_OAUTH_PERSISTENCE_INVALID\",\n      `The OAuth persistence adapter returned an invalid ${field}.`,\n    )\n  }\n  return value\n}\n\nfunction clientExpiration(clientInformation: StoredOAuthClientInformation): number | undefined {\n  const parsed = OAuthClientInformationFullSchema.safeParse(clientInformation)\n  const seconds = parsed.success ? parsed.data.client_secret_expires_at : undefined\n  if (seconds === undefined || seconds === 0) return undefined\n  return assertFiniteEpoch(seconds * 1_000, \"client expiration\")\n}\n\nfunction tokenExpiration(tokens: StoredOAuthTokens, now: number): number | undefined {\n  if (tokens.expires_in === undefined) return undefined\n  if (!Number.isFinite(tokens.expires_in) || tokens.expires_in < 0) {\n    throw new EnterpriseMcpOAuthContractError(","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/packages/enterprise-mcp-client/src/oauth-provider.ts#L24-L60","documentation":"assertFiniteEpoch() rejected a numeric epoch value produced by the OAuth persistence layer because it was not a finite, non-negative number. The error (EnterpriseMcpOAuthContractError, code MCP_OAUTH_PERSISTENCE_INVALID) names the offending field via the message (e.g. \"client expiration\" or \"token expiration\"). The library treats NaN/Infinity/negative expiration epochs as a contract violation by the persistence adapter, since such values cannot be compared against clock.now().","triggerScenarios":"clientExpiration() computes client_secret_expires_at * 1000 into a non-finite/negative value and passes it to assertFiniteEpoch; or the credentials record's expiresAt loaded from persistence is NaN/Infinity/negative (reached via tokenExpiration / clientInformation / tokens).","commonSituations":"Persistence adapter stores dates as strings or null and a numeric coercion yields NaN; clock skew or corrupted records producing negative epochs; client_secret_expires_at stored in ms instead of s (huge but finite — usually fine) or as a sentinel like -1 for \"never\".","solutions":["Fix the persistence adapter to store expiresAt / client_secret_expires_at as finite epoch numbers (milliseconds for record.expiresAt, seconds for client_secret_expires_at).","Use undefined (not -1/0-sentinels or NaN) when there is no expiration.","Validate/sanitize the numeric values when writing records, not just when reading.","Inspect the stored record for the named field to confirm it is a finite number ≥ 0."],"exampleFix":"// before (persistence adapter)\nexpiresAt: row.expires_at === null ? -1 : Date.parse(row.expires_at)\n// after\nexpiresAt: row.expires_at === null ? undefined : Date.parse(row.expires_at)","handlingStrategy":"validation","validationCode":"function isValidEpochMs(v: unknown): v is number {\n  return typeof v === \"number\" && Number.isFinite(v) && v >= 0;\n}\n// run on expiresAt / client_secret_expires_at before persisting","typeGuard":"function isFiniteEpoch(v: unknown): v is number {\n  return typeof v === \"number\" && Number.isFinite(v) && v >= 0;\n}","tryCatchPattern":"try {\n  await client.connect(input);\n} catch (e) {\n  if (e.code === \"MCP_OAUTH_PERSISTENCE_INVALID\") {\n    // audit the stored OAuth records for the named field and clear the corrupt entry\n  }\n  throw e;\n}","preventionTips":["Enforce numeric finite epoch columns (NOT NULL numeric) in the persistence store.","Use undefined — never -1 or NaN — for \"no expiration\".","Unit-test the persistence adapter's save/load round-trip for expiration fields.","Validate records at write time, not just at read time."],"tags":["oauth","persistence","contract-violation","validation"],"backgroundTag":"oauth-persistence-invalid","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}