{"record":{"id":"d1b473ec201d1324","repo":"paperclipai/paperclip","slug":"connector-binding-mismatch","errorCode":"CONNECTOR_BINDING_MISMATCH","errorMessage":"Paperclip Cloud credential binding did not match","messagePattern":"Paperclip Cloud credential binding did not match","errorType":"error_code","errorClass":"PaperclipCloudConnectorError","httpStatus":null,"severity":"error","filePath":"server/src/services/paperclip-cloud-connector.ts","lineNumber":303,"sourceCode":"    const definition = connectorProfileDefinition(profile);\n    const envelope = parseEnvelope(response.sealed, purpose, definition.provider, profile);\n    const credentials = unseal(\n      envelope,\n      sealKey,\n      config.instanceId,\n      config.environment,\n      definition.provider,\n      profile,\n      definition.scopes,\n    );\n    if (\n      credentials.instanceId !== config.instanceId\n      || credentials.environment !== config.environment\n      || credentials.subject !== subject\n      || credentials.companyId !== companyId\n      || credentials.provider !== definition.provider\n    ) {\n      throw new PaperclipCloudConnectorError(\"Paperclip Cloud credential binding did not match\", \"CONNECTOR_BINDING_MISMATCH\");\n    }\n    if (credentials.profile !== profile) {\n      throw new PaperclipCloudConnectorError(\"Paperclip Cloud connector profile binding did not match\", \"CONNECTOR_BINDING_MISMATCH\");\n    }\n    if (!sameStringSet(credentials.scopes, definition.scopes)) {\n      throw new PaperclipCloudConnectorError(\"Paperclip Cloud scope grant did not match\", \"REAUTHORIZATION_REQUIRED\");\n    }\n    return credentials;\n  }\n\n  return {\n    async getInstanceStatus(): Promise<\"active\" | \"suspended\" | \"removed\"> {\n      let response: ConnectorResponse;\n      try {\n        response = await call(\"status\", {\n          subject: \"instance-status\",\n          companyId: \"instance-status\",\n        });","sourceCodeStart":285,"sourceCodeEnd":321,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/paperclip-cloud-connector.ts#L285-L321","documentation":"In openCredentials (server/src/services/paperclip-cloud-connector.ts:303), after unsealing the connector credentials envelope, the code validates the credential bindings — instanceId, environment, subject, companyId, and provider must each match the current configuration and request context. Any mismatch throws PaperclipCloudConnectorError with code CONNECTOR_BINDING_MISMATCH. This prevents credentials minted for one instance/environment/subject/company/provider from being used in a different context (credential confusion / cross-tenant reuse).","triggerScenarios":"Calling claim() or refresh() where the unsealed credentials were issued for a different Paperclip instance id, environment, subject, companyId, or connector provider than the ones passed to openCredentials — e.g. the connector response's sealed envelope was created under another config.instanceId or the subject/companyId arguments changed since the credentials were minted.","commonSituations":"Restoring a database or copying sealed credentials between dev/staging/prod environments (environment or instanceId mismatch); pointing the instance at a different Paperclip Cloud account (subject/companyId mismatch); changing the connector provider definition while old credentials are still cached; shared cloud tenants across multiple self-hosted instances.","solutions":["Re-authorize the connector so fresh credentials are sealed for the current instanceId/environment/subject/companyId (run the connector connect/claim flow again).","Check server config (instanceId, environment) against the values used when the credentials were minted; align config or re-mint credentials — never hand-edit sealed envelopes.","Purge cached/stored sealed credentials for the connector so the next claim mints new, correctly-bound ones.","If this happens across restarts, verify the signing/seal key and instance identity are stable and not rotating, and that companyId/subject arguments at the call site match the connector registration."],"exampleFix":"// before (config drifted from minted credentials)\nconst config = { instanceId: \"inst-b\", environment: \"production\" }; // creds were sealed for inst-a/dev\nawait connector.claim(...); // CONNECTOR_BINDING_MISMATCH\n// after\n// re-run the connector authorization flow for inst-b/production so a new envelope is sealed,\n// or restore the original config the credentials were bound to:\nconst config = { instanceId: \"inst-a\", environment: \"development\" };\nawait connector.claim(...); // bindings match","handlingStrategy":"try-catch","validationCode":"// before claim/refresh, confirm local config matches the registered connector binding\nif (credentials && (credentials.instanceId !== config.instanceId || credentials.environment !== config.environment)) {\n  await reauthorizeConnector(); // mint fresh credentials instead of failing later\n}","typeGuard":"function isBoundToContext(\n  c: { instanceId: string; environment: string; subject: string; companyId: string; provider: string },\n  ctx: { instanceId: string; environment: string; subject: string; companyId: string; provider: string },\n): boolean {\n  return c.instanceId === ctx.instanceId && c.environment === ctx.environment\n    && c.subject === ctx.subject && c.companyId === ctx.companyId && c.provider === ctx.provider;\n}","tryCatchPattern":"try {\n  const creds = await connector.claim(args);\n} catch (err) {\n  if (err instanceof PaperclipCloudConnectorError && err.code === \"CONNECTOR_BINDING_MISMATCH\") {\n    await purgeStoredCredentials();\n    return await connector.claim(await startAuthorizationFlow(args)); // re-mint bound credentials\n  }\n  throw err;\n}","preventionTips":["Never copy sealed credentials or the DB between instances/environments; always re-authorize per instance.","Keep instanceId and environment stable across restarts; if they must change, plan a credential re-mint.","After changing connector provider definitions or cloud accounts, purge cached envelopes and re-run the connect flow.","Log the binding fields (instance, environment, provider) on mismatch to immediately identify which axis diverged."],"tags":["credentials","security","binding-mismatch","paperclip-cloud"],"backgroundTag":"credential-binding-mismatch","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}