{"record":{"id":"dc9ec2126d731759","repo":"mastra-ai/mastra","slug":"factory-workspace-github-credential-registration-i","errorCode":null,"errorMessage":"Factory workspace GitHub credential registration is no longer active.","messagePattern":"Factory workspace GitHub credential registration is no longer active\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mastracode/factory/src/workspace.ts","lineNumber":517,"sourceCode":"            evicted = (await mastra?.removeWorkspace?.(workspaceId)) === true;\n          } catch {\n            // Preserve the credential-replacement error and retry on the next reuse.\n          }\n          try {\n            await workspace.destroy();\n            evicted = true;\n          } catch {\n            // The pending registration keeps the workspace quarantined if cleanup also fails.\n          }\n          if (evicted && githubTokenInjectors.get(workspaceId) === registered) {\n            githubTokenInjectors.delete(workspaceId);\n            constructedWorkspaces.delete(workspaceId);\n          }\n        }\n        throw error;\n      }\n      if (registered && githubTokenInjectors.get(workspaceId) !== registered) {\n        throw new Error('Factory workspace GitHub credential registration is no longer active.');\n      }\n      return workspace;\n    };\n\n    let existing: Workspace | undefined;\n    try {\n      existing = mastra?.getWorkspaceById(workspaceId) as Workspace | undefined;\n    } catch {\n      // Not registered yet.\n      existing = undefined;\n    }\n    existing ??= constructedWorkspaces.get(workspaceId);\n    if (existing) {\n      existing.setToolsConfig(MASTRACODE_WORKSPACE_TOOLS);\n      // A materialization kicked off by another caller may still be running.\n      // Deliberately do NOT wait for it: a metadata-only resolution (thread\n      // list, messages, activity) must not block on the clone/setup that lazy\n      // materialization exists to avoid. Token reconciliation below no-ops","sourceCodeStart":499,"sourceCodeEnd":535,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/factory/src/workspace.ts#L499-L535","documentation":"After workspace construction, `reconcileRegisteredWorkspace` verifies that the GitHub credential registration made during setup is still the one stored for the workspaceId. If the map entry was replaced or deleted while materializing, the workspace's credentials can no longer be trusted, so creation fails fast.","triggerScenarios":"`createWorkspaceFactory` finishes constructing a workspace with a non-null `registered`, but `githubTokenInjectors.get(workspaceId)` returns a different registration object — another registration replaced it during construction.","commonSituations":"Two concurrent createWorkspaceFactory/reconciliation calls for the same workspaceId; a teardown/retirement of the workspace interleaved with creation; duplicate workspace factories built for one session.","solutions":["Retry workspace creation — the conflicting registration will have settled","Serialize workspace creation per workspaceId (avoid building the same factory concurrently)","Check for duplicate session/workspace lifecycle events triggering parallel reconciliation","Add logging on registerGithubTokenInjector to identify which path overwrote the registration"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"const registered = await registerWorkspaceCredentials(workspaceId);\nif (githubTokenInjectors.get(workspaceId) !== registered) {\n  // another reconciliation overwrote registration — retry creation serially\n  return createWorkspaceFactory(session, { lock: workspaceId });\n}","typeGuard":null,"tryCatchPattern":"try {\n  const ws = await createWorkspaceFactory(session);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('credential registration is no longer active')) {\n    const ws = await createWorkspaceFactory(session); // retry after the race settles\n  } else throw e;\n}","preventionTips":["Serialize createWorkspaceFactory calls per workspaceId (mutex/lock)","Do not retire or re-register a workspace while its factory is being constructed","Use a single reconciliation path for GitHub credential registration","Log registerGithubTokenInjector overwrites to detect concurrent writers"],"tags":["race-condition","workspace","github","factory"],"backgroundTag":"stale-credential-context","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}