{"record":{"id":"7f6802aff89e7410","repo":"calcom/cal.diy","slug":"invalid-credentials-for-userid-credential-userid","errorCode":null,"errorMessage":"Invalid credentials for userId ${credential.userId} and appId ${credential.appId}: ${parsedKey.error}","messagePattern":"Invalid credentials for userId (.+?) and appId (.+?): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/app-store/closecom/lib/CrmService.ts","lineNumber":72,"sourceCode":" *\n * Contact creation\n * Every contact in Close.com need to belong to a Lead. When creating a contact in\n * Close.com as part of this integration, a new generic Lead will be created in order\n * to assign every contact created by this process, and it is named \"From Cal.diy\"\n */\nclass CloseComCRMService implements CRM {\n  private integrationName = \"\";\n  private closeCom: CloseCom;\n  private log: typeof logger;\n\n  constructor(credential: CredentialPayload) {\n    this.integrationName = \"closecom_other_calendar\";\n    this.log = logger.getSubLogger({ prefix: [`[[lib] ${this.integrationName}`] });\n\n    const parsedKey = credentialSchema.safeParse(credential.key);\n\n    if (!parsedKey.success) {\n      throw new Error(\n        `Invalid credentials for userId ${credential.userId} and appId ${credential.appId}: ${parsedKey.error}`\n      );\n    }\n\n    // Initialize CloseCom client based on credential type\n    if (parsedKey.data.encrypted) {\n      // API key authentication\n      const decrypted = symmetricDecrypt(parsedKey.data.encrypted, CALENDSO_ENCRYPTION_KEY);\n      const { api_key } = JSON.parse(decrypted);\n      this.closeCom = new CloseCom(api_key);\n    } else if (parsedKey.data.access_token) {\n      // OAuth authentication\n      this.closeCom = new CloseCom(parsedKey.data.access_token, {\n        refresh_token: parsedKey.data.refresh_token,\n        expires_at: parsedKey.data.expires_at,\n        isOAuth: true,\n        userId: credential.userId!,\n      });","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/packages/app-store/closecom/lib/CrmService.ts#L54-L90","documentation":"Constructor of `CloseComCRMService` parses `credential.key` against `credentialSchema` (zod). If the key shape is wrong, it throws an Error whose message interpolates `credential.userId`, `credential.appId`, and the zod error. This stops CRM operations before they can send malformed auth to Close.","triggerScenarios":"A Close.com credential row whose `key` is missing required fields (e.g. no `access_token`/`refresh_token`/`expires_at` for OAuth, or no `encrypted` for API-key mode), has extra/renamed fields, or values of the wrong type — typically after an incomplete OAuth save or a schema migration.","commonSituations":"OAuth callback persisted a partial credential (token exchange succeeded but `key` written incompletely); schema changed without backfill; manual DB edit; older credential format from a previous app version.","solutions":["Read the interpolated `parsedKey.error` in the thrown message — it lists the exact failing field paths.","Have the user reconnect Close.com via OAuth so a fresh, schema-compliant credential is written.","If a schema change caused it, write a migration to backfill/rename fields on existing `closecom_crm` credentials.","Add a setup-time validation step so partial credentials are never persisted."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const parsed = credentialSchema.safeParse(credential.key);\nif (!parsed.success) {\n  const issues = parsed.error.issues.map((i) => i.path.join(\".\") + \": \" + i.message).join(\"; \");\n  throw new Error(`Close.com credential invalid: ${issues}`);\n}","typeGuard":"function isCloseCredentialKey(k: unknown) {\n  return credentialSchema.safeParse(k).success;\n}","tryCatchPattern":"try {\n  new CloseComCRMService(credential);\n} catch (e) {\n  if (e instanceof Error && /Invalid credentials/.test(e.message)) {\n    await flagCredentialForReconnection(credential.id);\n    return null;\n  }\n  throw e;\n}","preventionTips":["Validate the credential key shape at OAuth-save time.","Run schema backfills when extending credentialSchema.","Surface zod issue paths in the error message for fast triage.","Add a setup wizard that blocks incomplete credential persistence."],"tags":["validation","zod","credentials","closecom","crm"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}