{"record":{"id":"455f318ed4bf340f","repo":"deepseek-ai/deepseek-harness","slug":"sessions-selectsubagent-address-childsessionid","errorCode":null,"errorMessage":"sessions.selectSubagent: ${address.childSessionId} is not a healthy catalog child","messagePattern":"sessions\\.selectSubagent: (.+?) is not a healthy catalog child","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/client/runtime/src/client/sessions/manager.ts","lineNumber":212,"sourceCode":"        ? false\n        : this.catalogs.get(address.parentSessionId)?.parentAvailable ?? false,\n    )\n    this.selected = sessionId\n    // Looking at the session consumes its completion reminder (dot clears).\n    this.completedNotifications.delete(sessionId)\n    void this.refreshSubagents(sessionId)\n    this.notifier.notifyNow()\n  }\n\n  /**\n   * Select a healthy child through its durable direct-parent address.\n   * @param address - catalog-derived parent and child ids.\n   */\n  selectSubagent(address: SubagentAddress): void {\n    const catalog = this.catalogs.get(address.parentSessionId)\n    const entry = catalog?.entries.find(candidate => candidate.id === address.childSessionId)\n    if (entry === undefined || entry.kind !== 'child' || entry.mode !== address.mode) {\n      throw new Error(`sessions.selectSubagent: ${address.childSessionId} is not a healthy catalog child`)\n    }\n    this.addresses.set(address.childSessionId, address)\n    this.sessions.get(address.childSessionId)?.configureSubagent(address, catalog?.parentAvailable ?? false)\n    this.selected = address.childSessionId\n    this.completedNotifications.delete(address.childSessionId)\n    void this.refreshSubagents(address.childSessionId)\n    this.notifier.notifyNow()\n  }\n\n  /** Clear the selection (the layout falls to the no-session view state). */\n  clearSelection(): void {\n    this.selected = undefined\n    this.notifier.notifyNow()\n  }\n\n  /**\n   * Return the durable catalog address retained for one child.\n   * @param sessionId - possible addressed child id.","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/deepseek-ai/deepseek-harness/blob/b150a551b8d465e31e418e1b2eaf5e79bbb7d28e/packages/client/runtime/src/client/sessions/manager.ts#L194-L230","documentation":"SessionsManager.selectSubagent re-validates a SubagentAddress against the cached subagent catalog of its parent session before selecting. It throws when no catalog is loaded for address.parentSessionId (so the entry lookup yields undefined), when the child id is absent from catalog.entries, when the entry's kind is not 'child', or when entry.mode differs from address.mode. The guard exists because addresses are durable and catalog-derived: a stale, persisted, or hand-built address must never gain selection authority over the live roster truth.","triggerScenarios":"Calling sessions.openSubagent/selectSubagent with (a) an address captured from an earlier catalog snapshot after the child finished, changed mode, or was removed by a roster change; (b) an address for a parent whose catalog has not been pulled yet (this.catalogs has no entry, so entry is undefined); (c) a mode mismatch, e.g. address.mode 'agent' against entry.mode 'task'; (d) a wrong parentSessionId so the lookup scans another parent's catalog or none.","commonSituations":"Caching SubagentAddress values across catalog refreshes or page reloads; clicking a subagent row in the same tick a roster change removes it; restoring a persisted address at startup before refreshSubagents has pulled the parent catalog; test fixtures hand-crafting addresses instead of deriving them from catalog entries.","solutions":["Re-derive the address from the current catalog, or use manager.select(childSessionId) which resolves a retained or catalog-derived address leniently through navigationAddress and only throws when the session is neither listed nor addressable","Ensure the parent catalog is loaded before offering subagent navigation: open or refresh the parent session first and wait for its catalog pull","Copy the mode field from the catalog entry when constructing the address, never from a constant or persisted string","Invalidate cached addresses on roster/catalog change events and re-read entries before any selection"],"exampleFix":"// before — replaying an address captured earlier\nmanager.selectSubagent(staleAddress)\n\n// after — lenient selection that re-derives the address from loaded catalogs\nmanager.select(staleAddress.childSessionId)","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  manager.selectSubagent(address)\n} catch (error) {\n  if (error instanceof Error && error.message.startsWith('sessions.selectSubagent:')) {\n    // stale address: fall back to lenient selection, which re-derives from loaded catalogs\n    manager.select(address.childSessionId)\n  } else {\n    throw error\n  }\n}","preventionTips":["Derive SubagentAddress values from the live catalog entry at click time — entry.id, entry.mode, and the owning parentSessionId","Drop cached addresses on roster or catalog changes instead of persisting them across reloads","Confirm the parent session's catalog finished loading before offering subagent navigation","Never hand-write mode strings; copy entry.mode"],"tags":["subagent","session-selection","catalog","stale-state"],"backgroundTag":"stale-reference","analyzedSha":"b150a551b8d465e31e418e1b2eaf5e79bbb7d28e","analyzedAt":"2026-08-24T18:12:29.105Z","schemaVersion":2},"datasetVersion":"2026-08-24T22:17:12.610Z"}