{"record":{"id":"6fd95833be0caaec","repo":"deepseek-ai/deepseek-harness","slug":"session-entry-id-was-already-announced","errorCode":null,"errorMessage":"session \"${entry.id}\" was already announced","messagePattern":"session \"(.+?)\" was already announced","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/session/src/index.ts","lineNumber":971,"sourceCode":"    // later same-id lifecycle.\n    /* v8 ignore next -- enter() rejects replacement while this single-shot detach capability is live. */\n    if (this.store.get(entry.id) !== entry) return\n    this.store.delete(entry.id)\n    attachments.delete(entry.session)\n    if (entry.announced) this.emitDisposed(entry)\n  }\n\n  /** Emit `session/created` exactly once for an {@link enter}ed session (with\n   * the carrier {@link enter} captured). Separate from {@link enter} so the\n   * caller can yield the detach disposer first (rollback safety — see\n   * {@link enter}).\n   * @param session - the entered session to announce to listeners.\n   * @throws if the session is not live or its announcement already began,\n   *   including a reentrant call from a creation listener. */\n  announce(session: Session): void {\n    const entry = this.liveEntryFor(session)\n    if (entry.announced || entry.announcing) {\n      throw new Error(`session \"${entry.id}\" was already announced`)\n    }\n    // Mark before emit: Cordis emit may deliver to earlier listeners and then\n    // throw. Rollback must still pair that partial creation with disposal, and\n    // a listener cannot recursively create a second lifecycle edge.\n    entry.announced = true\n    const callbackArgs: unknown[] = [session]\n    entry.announcing = true\n    try {\n      const callbacks = collectSessionCallbacks(this.ctx, [entry.carrier, 'session/created', session])\n      for (const callback of callbacks) {\n        // Synchronous throws intentionally propagate and veto publication; the\n        // yielded detach then emits the paired disposal edge. An async function\n        // is nevertheless assignable to a void listener, so observe its returned\n        // promise: rejection is too late to roll back and must be logged instead\n        // of becoming unhandled.\n        const returned: unknown = callback(...callbackArgs)\n        void Promise.resolve(returned).catch((error: unknown) => {\n          this.ctx.logger.warn(`session \"${entry.id}\": session/created listener rejected: ${String(error)}`)","sourceCodeStart":953,"sourceCodeEnd":989,"githubUrl":"https://github.com/deepseek-ai/deepseek-harness/blob/b150a551b8d465e31e418e1b2eaf5e79bbb7d28e/packages/core/session/src/index.ts#L953-L989","documentation":"Error \"session \"${entry.id}\" was already announced\" thrown in deepseek-ai/deepseek-harness.","triggerScenarios":"Thrown at packages/core/session/src/index.ts:971 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":[],"exampleFix":null,"handlingStrategy":null,"validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"b150a551b8d465e31e418e1b2eaf5e79bbb7d28e","analyzedAt":"2026-08-24T18:12:29.105Z","schemaVersion":2},"datasetVersion":"2026-08-24T22:17:12.610Z"}