hcengineering/platform · error

Couldn't find contact by id + channel.attachedTo

Error message

Couldn't find contact by id + channel.attachedTo

What it means

sendMsg throws when the PContact referenced by channel.attachedTo cannot be found via findOne in the channel's attachedToClass. To add the Telegram contact before sending, the message needs the contact's name split into first/last name; a missing contact record breaks the outgoing message pipeline (txCreateDoc → res → sendNewMsgs).

Source

Thrown at services/telegram/pod-telegram/src/workspace.ts:323

    if (rec === undefined || rec?.phone === undefined) return
    const client = this.clients.get(rec.phone)

    if (client === undefined) {
      throw Error('Failed to find connection')
    }

    const channel = this.channelsById.get(msg.attachedTo as Ref<Channel>)
    if (channel === undefined) return
    const target = normalizeValue(channel.value)

    const importRequired = await client.conn.isContactImportRequired(target)

    if (importRequired) {
      const contact = await this.client.findOne<PContact>(channel.attachedToClass, {
        _id: channel.attachedTo as Ref<PContact>
      })
      if (contact === undefined) {
        throw new Error("Couldn't find contact by id" + channel.attachedTo)
      }

      await client.conn.addContact({
        lastName: getLastName(contact.name),
        firstName: getFirstName(contact.name),
        phone: target
      })
    }

    const { message, entities } = platformToTelegram(msg.content)
    const files = await this.getFiles(msg)
    if (files.length < 2) {
      const res = await client.conn.sendMsg(target, message, entities, files.shift())
      const user = await res.getChat()

      if (user?.className !== 'User') {
        return
      }

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Inspect the channel document and re-create/restore the missing PContact with _id = channel.attachedTo.
  2. Delete the orphaned channel so the message queue stops retrying the broken target.
  3. Add a cascade rule so deleting a contact also removes (or detaches) its channels.
  4. Wrap the send in a guard that logs and skips (or re-links) channels with missing contacts instead of throwing.

Example fix

// before
if (contact === undefined) {
  throw new Error("Couldn't find contact by id" + channel.attachedTo)
}
// after
if (contact === undefined) {
  ctx.warn('Skipping channel with missing contact', { channel: channel._id, contact: channel.attachedTo })
  return // or re-link the channel to an existing contact
}
Defensive patterns

Strategy: type-guard

Validate before calling

const contact = await client.findOne<PContact>(channel.attachedToClass, { _id: channel.attachedTo as Ref<PContact> })
if (contact === undefined) {
  throw new Error(`Channel ${channel._id} references missing contact ${channel.attachedTo}; fix data before sending`)
}

Type guard

function channelHasContact(channel: Channel, contact: PContact | undefined): contact is PContact {
  return contact !== undefined && channel.attachedTo === (contact as PContact)._id
}

Try / catch

try {
  await sendMessage(channel, msg)
} catch (err) {
  if (err instanceof Error && err.message.startsWith("Couldn't find contact by id")) {
    ctx.warn('Skipping channel with missing contact', { channel: channel._id })
  } else throw err
}

Prevention

When it happens

Trigger: A message is sent on a channel whose attachedTo points to a deleted or never-created PContact document; data inconsistency between channels and contacts (e.g. contact removed by a migration or cascade delete) surfaces when sendNewMsgs processes the queue.

Common situations: Contacts deleted while their channels remained, partially completed imports/integrations, or workspace data restored from a backup where contacts were lost but channels survived.

Related errors


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/30461ccfbea81412. Report an issue: GitHub.