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
- Restore or recreate the missing Training with the referenced _id
- Delete or reattach the orphaned TrainingRequest to a valid Training
- Add cascading delete so removing a Training also removes its TrainingRequests
- 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
- Enable cascading deletes: remove TrainingRequests when a Training is deleted
- Run periodic orphan checks for requests whose attachedTo no longer resolves
- Prefer a fallback label over throwing inside presenters, which run in trigger contexts
- Validate attachedTo references on data import/migration
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
- attribute presenter not found for ${JSON.stringify(preserveK
- object presenter not found for class=${_class}, preserve key
- lookup class does not provided for ${key.key}
- presenter not found for ${_class}
- AccountNotFound
AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29).
Data as JSON: /api/errors/1a40ddaa3bf9e7da.
Report an issue: GitHub.