hcengineering/platform · error

Training #${request.attachedTo} not found

Error message

Training #${request.attachedTo} not found

What it means

TrainingRequestTextPresenter renders a human-readable text for a TrainingRequest by looking up the parent Training via request.attachedTo. If no Training object with that _id exists, it throws 'Training #<id> not found' because the presenter cannot produce a label without the parent document.

Source

Thrown at server-plugins/training-resources/src/functions/TrainingRequestTextPresenter.ts:29

//
// See the License for the specific language governing permissions and
// limitations under the License.
//

import type { TriggerControl } from '@hcengineering/server-core'
import type { Presenter } from '@hcengineering/server-notification'
import type { TrainingRequest } from '@hcengineering/training'
import training from '@hcengineering/training'

/** @public */
export const TrainingRequestTextPresenter: Presenter<TrainingRequest> = async (
  request: TrainingRequest,
  control: TriggerControl
) => {
  const trainingObject = (await control.findAll(control.ctx, training.class.Training, { _id: request.attachedTo }))[0]

  if (trainingObject === undefined) {
    throw new Error(`Training #${request.attachedTo} not found`)
  }

  return `${trainingObject.code} • ${trainingObject.title} • Revision ${trainingObject.revision}`
}

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Restore or recreate the missing Training with the referenced _id
  2. Delete or reattach the orphaned TrainingRequest to a valid Training
  3. Add cascading delete so removing a Training also removes its TrainingRequests
  4. Make the presenter tolerant: return a fallback string instead of throwing for missing parents

Example fix

// before
if (trainingObject === undefined) {
  throw new Error(`Training #${request.attachedTo} not found`)
}
// after
if (trainingObject === undefined) {
  return `Unknown training (${request.attachedTo})`
}
Defensive patterns

Strategy: validation

Validate before calling

const parent = (await control.findAll(control.ctx, training.class.Training, { _id: request.attachedTo }))[0]
if (parent === undefined) return `Unknown training (${request.attachedTo})`

Type guard

function trainingExists(t: Training | undefined): t is Training {
  return t !== undefined
}

Try / catch

try {
  const text = await presenter(request, control)
} catch (err) {
  if (String((err as Error).message).endsWith('not found')) {
    return `Unavailable training (${request.attachedTo})`
  }
  throw err
}

Prevention

When it happens

Trigger: Trigger control invokes the presenter function for a TrainingRequest whose attachedTo points at a Training that was deleted, or whose _id is invalid/never created (dangling reference).

Common situations: A Training was deleted while its TrainingRequests remained; data import/migration left orphaned requests; request created before the training commit landed; wrong attachedTo id from a client bug.

Related errors


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