{"record":{"id":"e479602e07d65f77","repo":"mastra-ai/mastra","slug":"this-constructor-name-find-requires-connect","errorCode":null,"errorMessage":"${this.constructor.name}: find() requires connect() to adopt the handle it returns.","messagePattern":"(.+?): find\\(\\) requires connect\\(\\) to adopt the handle it returns\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/workspace/sandbox/mastra-sandbox.ts","lineNumber":273,"sourceCode":"    this._onStop = options.onStop;\n    this._onDestroy = options.onDestroy;\n    this.#env = { ...options.env };\n\n    // Shadow start() with the lifecycle wrapper (same pattern as\n    // SandboxProcessManager) so DIRECT start() calls get the same coalescing,\n    // status handling, and onStart hook as `_start()`/`ensureRunning()`.\n    const hasStartOverride = this.start !== MastraSandbox.prototype.start;\n    this._implStart = this.start.bind(this);\n    this.start = () => this._start();\n    // Rung selection: a subclass `start()` override wins; otherwise the\n    // primitives drive acquisition when `create()` is implemented. Anything\n    // declared as a class field is invisible here and lands on the base\n    // `start()`, which throws.\n    this._useAcquisitionPrimitives = !hasStartOverride && typeof this.create === 'function';\n    // A handle nobody adopts would still report `outcome: 'connected'`, so the\n    // sandbox would look reconnected while running against nothing.\n    if (this._useAcquisitionPrimitives && typeof this.find === 'function' && typeof this.connect !== 'function') {\n      throw new Error(`${this.constructor.name}: find() requires connect() to adopt the handle it returns.`);\n    }\n\n    // Automatically create MountManager if subclass implements mount()\n    if (this.mount) {\n      this.mounts = new MountManager({\n        mount: this.mount.bind(this),\n        logger: this.logger,\n      });\n    }\n\n    // Wire up process manager if provided\n    if (options.processes) {\n      const pm = options.processes;\n      // Set the sandbox back-reference. The process manager reads this\n      // lazily (at call time), so it's fine that the subclass constructor\n      // hasn't finished yet.\n      pm.sandbox = this;\n      this.processes = pm;","sourceCodeStart":255,"sourceCodeEnd":291,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/workspace/sandbox/mastra-sandbox.ts#L255-L291","documentation":"The MastraSandbox base constructor enforces that acquisition primitives are implemented in pairs: if a subclass defines `find()` (to locate an existing running sandbox) as part of the create/find/connect acquisition flow, it must also define `connect()` to adopt the handle that find() returns. A find() without connect() would let a handle be discovered but never adopted, so the sandbox would falsely report `outcome: 'connected'` while operating on nothing.","triggerScenarios":"Subclassing MastraSandbox and overriding `find()` (without overriding `start()`, which would disable acquisition primitives) while not implementing `connect()`; typically a partial or in-progress implementation of the acquisition protocol.","commonSituations":"Implementing a custom remote/container sandbox that reuses existing instances; refactoring that removed connect() but left find(); copying a partial example subclass.","solutions":["Implement `connect(handle)` in the subclass so find()'s discovered handle is adopted","Remove the `find()` override if the class doesn't need the acquisition flow","If you intended no acquisition primitives, override `start()` instead so the pair check is skipped"],"exampleFix":"// before\nclass RemoteSandbox extends MastraSandbox {\n  async find() { return probeExisting(); }\n  // connect() missing -> constructor throws\n}\n// after\nclass RemoteSandbox extends MastraSandbox {\n  async find() { return probeExisting(); }\n  async connect(handle) { this._handle = handle; await this.attach(handle); }\n}","handlingStrategy":"type-guard","validationCode":"type AcquisitionSandbox = { find: unknown } & { connect: (h: unknown) => Promise<unknown> };\nfunction hasBalancedAcquisition(s: object): s is AcquisitionSandbox {\n  const hasFind = 'find' in s && typeof (s as any).find === 'function';\n  const hasConnect = 'connect' in s && typeof (s as any).connect === 'function';\n  return !hasFind || hasConnect;\n}","typeGuard":"function definesAcquisitionPair(ctor: new (...a: any[]) => object): boolean {\n  const proto = ctor.prototype as Record<string, unknown>;\n  const hasFind = typeof proto.find === 'function';\n  const hasConnect = typeof proto.connect === 'function';\n  return !hasFind || hasConnect;\n}","tryCatchPattern":"try {\n  sandbox = new RemoteSandbox(opts);\n} catch (err) {\n  if (/find\\(\\) requires connect\\(\\)/.test(String(err?.message))) {\n    throw new Error(`${RemoteSandbox.name}: implement connect() to adopt find()'s handle, or remove find()`);\n  }\n  throw err;\n}","preventionTips":["Add a compile-time check: `type MustImplementConnect = find extends () => any ? connect : never`","Write a unit test that instantiates every sandbox subclass to catch contract violations at CI time","When implementing the acquisition flow, scaffold find/connect/create together, never separately"],"tags":["typescript","subclassing","sandbox","contract-violation"],"backgroundTag":"subclass-contract-violation","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}