{"record":{"id":"5db257e341efec57","repo":"hcengineering/platform","slug":"failed-to-create-channel-for-normalizedemail-in","errorCode":null,"errorMessage":"Failed to create channel for ${normalizedEmail} in space ${space}: ${err instanceof Error ? err.message : String(err)}","messagePattern":"Failed to create channel for (.+?) in space (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"services/mail/mail-common/src/channel.ts","lineNumber":104,"sourceCode":"\n      if (channel != null) {\n        this.ctx.info('Using existing channel', { me: normalizedEmail, space, channel: channel._id })\n        return channel._id as Ref<Card>\n      }\n\n      return await this.createNewChannel(space, participants, normalizedEmail, personId)\n    } catch (err) {\n      this.ctx.error('Failed to create channel', {\n        me: normalizedEmail,\n        space,\n        workspace: this.workspace,\n        error: err instanceof Error ? err.message : String(err)\n      })\n\n      // Remove failed lookup from cache\n      this.cache.delete(`${space}:${normalizedEmail}`)\n\n      throw new Error(\n        `Failed to create channel for ${normalizedEmail} in space ${space}: ${err instanceof Error ? err.message : String(err)}`\n      )\n    }\n  }\n\n  private async createNewChannel (\n    space: Ref<Space>,\n    participants: PersonId[],\n    email: string,\n    personId: PersonId\n  ): Promise<Ref<Card>> {\n    const normalizedEmail = normalizeEmail(email)\n    const mutexKey = `channel:${this.workspace}:${space}:${normalizedEmail}`\n    const releaseLock = await createMutex.lock(mutexKey)\n\n    try {\n      // Double-check that channel doesn't exist after acquiring lock\n      const existingChannel = await this.client.findOne(mail.tag.MailThread, { title: normalizedEmail })","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/services/mail/mail-common/src/channel.ts#L86-L122","documentation":"fetchOrCreateChannel looks up a mail channel for a normalized email within a space, and if absent creates one. When the creation itself fails (the wrapped err — DB errors, permission issues, invalid space/email, transient conflicts), the original error is removed from cache and rethrown wrapped in this descriptive Error naming the email and space.","triggerScenarios":"Calling getOrCreateChannel where the underlying createNewChannel/factory call throws — e.g. the space doesn't exist, database connectivity failure, or a concurrent request racing to create the same channel.","commonSituations":"Inbound mail processing for an address whose space was deleted; transient DB outages during mail ingestion; duplicate concurrent webhook deliveries creating the same channel simultaneously; misconfigured mail routing producing emails for non-existent spaces.","solutions":["Inspect the wrapped err.message (embedded in this message) to find the root cause — it names the underlying failure.","Verify the space exists and the email domain/address is valid before calling getOrCreateChannel.","Retry on transient DB errors — the failed cache entry is already evicted so a retry re-attempts creation.","Add a uniqueness-safe retry or upsert in createNewChannel to tolerate concurrent creation races."],"exampleFix":"// before\nconst channel = await getOrCreateChannel(ctx, space, email) // throws on race\n// after\nlet channel\ntry {\n  channel = await getOrCreateChannel(ctx, space, email)\n} catch (err) {\n  await new Promise(r => setTimeout(r, 200))\n  channel = await getOrCreateChannel(ctx, space, email)\n}","handlingStrategy":"retry","validationCode":"const spaceExists = await findSpace(ctx, space)\nif (spaceExists === undefined) {\n  throw new Error(`Space ${space} does not exist; not attempting channel creation for ${email}`)\n}","typeGuard":"null","tryCatchPattern":"try {\n  channel = await getOrCreateChannel(ctx, space, email)\n} catch (err) {\n  if (err.message.startsWith('Failed to create channel')) {\n    console.error(`Channel creation failed (root cause: ${err.message}); retrying once`)\n    await sleep(200)\n    channel = await getOrCreateChannel(ctx, space, email)\n  } else throw err\n}","preventionTips":["Verify the space exists before mail ingestion routes to it.","Retry transient failures — the failed lookup is evicted from cache, so retries re-attempt creation.","Make channel creation idempotent to survive concurrent deliveries of the same email."],"tags":["mail","channel-creation","database","concurrency"],"backgroundTag":"channel-creation-failed","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}