{"record":{"id":"16512087998d6769","repo":"mastra-ai/mastra","slug":"mastrafactory-integration-integration-id-sig","errorCode":null,"errorMessage":"MastraFactory: integration '${integration.id}' signs OAuth state and requires a replica-stable state secret, but none is configured. Set 'stateSecret' on the factory config.","messagePattern":"MastraFactory: integration '(.+?)' signs OAuth state and requires a replica-stable state secret, but none is configured\\. Set 'stateSecret' on the factory config\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mastracode/factory/src/factory.ts","lineNumber":603,"sourceCode":"                  ),\n              }\n            : {}),\n          messageReader: {\n            listMessages: async input => {\n              const memory = await storage.getMastraStorage().getStore('memory');\n              return memory ? memory.listMessages(input) : { messages: [], hasMore: false };\n            },\n          },\n        })\n      : undefined;\n\n    // Boot assertion: an active integration that signs OAuth `state` needs a\n    // replica-stable signer — a per-process random secret silently breaks the\n    // OAuth callback on any replica that didn't sign the state. Fail loud now\n    // instead. (The built-ins also assert this inside their readiness gates.)\n    for (const { integration } of integrationRegistrations) {\n      if (integration.requiresStableStateSigner && !stateSigner.stable) {\n        throw new Error(\n          `MastraFactory: integration '${integration.id}' signs OAuth state and requires a ` +\n            `replica-stable state secret, but none is configured. Set 'stateSecret' on the factory config.`,\n        );\n      }\n    }\n\n    // The SDK needs to know which backend the injected Mastra store uses\n    // (its own `instanceof` detection breaks when the dependency graph holds\n    // duplicate package copies). Resolve it by walking the FactoryStorage\n    // prototype chain by class name — the factory can't import the concrete\n    // classes since '@mastra/pg' / '@mastra/libsql' are the user's choice.\n    const mastraStorageBackend = (() => {\n      for (let proto = Object.getPrototypeOf(storage); proto; proto = Object.getPrototypeOf(proto)) {\n        if (proto.constructor?.name === 'PgFactoryStorage') return 'pg' as const;\n        if (proto.constructor?.name === 'LibSQLFactoryStorage') return 'libsql' as const;\n      }\n      return undefined;\n    })();","sourceCodeStart":585,"sourceCodeEnd":621,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/factory/src/factory.ts#L585-L621","documentation":"Integrations that sign OAuth 'state' parameters require a replica-stable secret: a per-process random signer breaks the OAuth callback on any replica that did not sign the state. During prepare(), if an active integration sets requiresStableStateSigner and the configured state signer is not stable (no stateSecret configured), the factory fails at boot rather than at first OAuth flow.","triggerScenarios":"Configuring an integration (e.g. GithubIntegration) whose requiresStableStateSigner is true, while the factory config lacks stateSecret — so stateSigner.stable is false at boot.","commonSituations":"Deploying behind multiple replicas/instances without a shared secret; local dev working because one process signs and verifies, then breaking in production; forgetting the env var that feeds stateSecret when moving between environments.","solutions":["Set stateSecret in the factory config: stateSecret: process.env.FACTORY_STATE_SECRET, sourced from a stable secret store.","Provision the same secret across all replicas/instances so any replica can verify state signed by another.","Add the env var to your deployment platform's secret manager and restart all instances after adding it.","For local dev, generate one long random value and commit it to .env (gitignored) so dev behavior matches production."],"exampleFix":"// before\nconst factory = new MastraFactory({\n  storage,\n  integrations: [new GithubIntegration()],\n});\n\n// after\nconst factory = new MastraFactory({\n  storage,\n  stateSecret: process.env.FACTORY_STATE_SECRET,\n  integrations: [new GithubIntegration()],\n});","handlingStrategy":"validation","validationCode":"if (!process.env.FACTORY_STATE_SECRET || process.env.FACTORY_STATE_SECRET.length < 32) {\n  throw new Error('FACTORY_STATE_SECRET must be set (32+ chars) for integrations that sign OAuth state');\n}\nconst config = { ...baseConfig, stateSecret: process.env.FACTORY_STATE_SECRET };","typeGuard":"function hasStableStateSecret(config) {\n  return typeof config.stateSecret === 'string' && config.stateSecret.length >= 32;\n}\nif (!hasStableStateSecret(factoryConfig)) throw new Error('Set a replica-stable stateSecret before boot');","tryCatchPattern":"try {\n  await factory.prepare();\n} catch (err) {\n  if (err.message.includes('replica-stable state secret')) {\n    throw new Error('Deploy config error: FACTORY_STATE_SECRET missing — OAuth will break across replicas', { cause: err });\n  }\n  throw err;\n}","preventionTips":["Set the secret from a shared secret manager, identical on every replica","Validate required env vars at boot before constructing the factory","Document stateSecret as required whenever enabling state-signing integrations","Rotate the secret only via a dual-accept window to avoid invalidating in-flight OAuth flows"],"tags":["oauth","configuration","security","secrets","replication"],"backgroundTag":"missing-env-var","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}