mastra-ai/mastra · error · MastraError
EXPERIMENT_HAS_NO_TARGET
EXPERIMENT_HAS_NO_TARGET
Error message
Experiment ${experiment.id} has no target; results must be ingested via submitExperimentResult What it means
executeExperimentItem refuses to run an experiment whose record has targetType===null or targetId===null. Experiments that are not linked to an agent/workflow target are 'externally ingested': their results must be supplied via submitExperimentResult, not executed locally. Throwing MastraError EXPERIMENT_HAS_NO_TARGET prevents silently executing against nothing.
Source
Thrown at packages/core/src/datasets/experiment/item.ts:74
* `(experimentId, itemId, attempt)` so a retried caller (e.g. a retried
* Temporal activity) converges on a single row.
*
* Scorer precedence mirrors the in-process runner: experiment `scorerIds` →
* item `scorerIds` → dataset `scorerIds` → none. An item scorer reference to
* an unregistered scorer is a deterministic preflight failure
* ({@link EXPERIMENT_ITEM_SCORER_NOT_FOUND}): the target is not executed and
* the error is recorded on the row.
*
* Deliberately excludes the runner's loop-level concerns: hooks, event
* dispatch, tool mocks, and internal retries — retries/timeouts belong to the
* caller's orchestrator.
*/
export async function executeExperimentItem(args: ExecuteExperimentItemArgs): Promise<ExecuteExperimentItemOutput> {
const { mastra, experiment, item } = args;
const attempt = args.attempt ?? 0;
if (experiment.targetType === null || experiment.targetId === null) {
throw new MastraError({
id: 'EXPERIMENT_HAS_NO_TARGET',
text: `Experiment ${experiment.id} has no target; results must be ingested via submitExperimentResult`,
domain: 'STORAGE',
category: 'USER',
});
}
const resolved = await resolveTarget(
mastra,
experiment.targetType,
experiment.targetId,
experiment.agentVersion ?? undefined,
);
if (!resolved) {
throw new MastraError({
id: 'EXPERIMENT_TARGET_NOT_FOUND',
text: `Target not found: ${experiment.targetType} "${experiment.targetId}"`,
domain: 'STORAGE',View on GitHub (pinned to 75dd419e61)
Solutions
- Pass targetType and targetId (e.g. an agent or workflow id) when creating the experiment if you intend to execute it locally.
- If the experiment is intentionally target-less, ingest results with submitExperimentResult instead of runExperimentItem.
- Check experiment.targetType/targetId before calling runExperimentItem and branch on it.
- Verify the experiment record in storage wasn't created/migrated with null target fields.
Example fix
// before
await runExperimentItem({ mastra, experiment, item }); // experiment.targetId === null
// after
if (experiment.targetId == null) {
await submitExperimentResult({ mastra, experimentId: experiment.id, input: item.input, output } );
} else {
await runExperimentItem({ mastra, experiment, item });
} Defensive patterns
Strategy: validation
Validate before calling
if (experiment.targetType == null || experiment.targetId == null) {
// route to submitExperimentResult instead of local execution
} Type guard
function hasTarget(e: { targetType: string | null; targetId: string | null }): e is { targetType: string; targetId: string } {
return e.targetType !== null && e.targetId !== null;
} Try / catch
try {
await runExperimentItem({ mastra, experiment, item });
} catch (err) {
if (err?.id === 'EXPERIMENT_HAS_NO_TARGET') return submitExperimentResult({ mastra, experimentId: experiment.id, ... });
throw err;
} Prevention
- Always set targetType/targetId when creating experiments you intend to execute locally.
- Branch explicitly: target-less experiments are ingest-only by design.
- Validate experiment records coming from storage/HTTP before executing.
- Document in your pipeline that manual/external experiments must use submitExperimentResult.
When it happens
Trigger: Calling runExperimentItem (directly or via local experiment execution) for an experiment created without a target, e.g. created for manual/external result ingestion.
Common situations: Creating an experiment via the datasets API/HTTP without passing targetId/targetType, then attempting to execute it; experiments seeded for CI-ingested eval results; migrating experiments where the target linkage was lost.
Related errors
- Google RBAC roleMapping is required.
- Cookie password must be at least 32 characters. Set OKTA_COO
- heartbeatMs must be a finite number no greater than ${MAX_TI
- Expected a PEM-encoded public key or certificate string for
- terminationGraceMs must be greater than zero.
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/64e899bcc38c429f.
Report an issue: GitHub.