mastra-ai/mastra · error · FactoryRuleValidationError

Factory skill cancelInFlight must be a boolean.

Error message

Factory skill cancelInFlight must be a boolean.

What it means

The optional cancelInFlight field on an invoke-skill decision controls whether an in-flight run of the same key is cancelled; it must be a real boolean when provided. Passing a truthy/falsy non-boolean (strings 'true'/'false', 0/1, null) is rejected so downstream logic can rely on strict boolean semantics.

Source

Thrown at mastracode/factory/src/rules/validation.ts:297

      assertExactKeys(
        value,
        ['type', 'idempotencyKey', 'role', 'skillName', 'prompt', 'arguments', 'precedingMessage', 'cancelInFlight'],
        'Factory invoke skill decision',
      );
      // A run activates a skill or carries a prompt, never both: they are two
      // ways to author the same kickoff message, so accepting both would leave
      // the dispatcher picking a winner.
      if ((value.skillName === undefined) === (value.prompt === undefined)) {
        throw new FactoryRuleValidationError('Factory skill invocation needs exactly one of skillName or prompt.');
      }
      const args = optionalBoundedString(value.arguments, 'Factory skill arguments', MAX_ARGUMENTS_LENGTH);
      const precedingMessage = optionalBoundedString(
        value.precedingMessage,
        'Factory skill preceding message',
        MAX_MESSAGE_LENGTH,
      );
      if (value.cancelInFlight !== undefined && typeof value.cancelInFlight !== 'boolean') {
        throw new FactoryRuleValidationError('Factory skill cancelInFlight must be a boolean.');
      }
      return {
        type,
        ...commonCommitFields(value),
        role: boundedString(value.role, 'Factory skill role', MAX_ROLE_LENGTH, IDENTIFIER_RE),
        ...(value.skillName === undefined
          ? { prompt: boundedString(value.prompt, 'Factory skill prompt', MAX_MESSAGE_LENGTH) }
          : { skillName: boundedString(value.skillName, 'Factory skill name', MAX_SKILL_NAME_LENGTH, SKILL_NAME_RE) }),
        ...(args ? { arguments: args } : {}),
        ...(precedingMessage ? { precedingMessage } : {}),
        ...(value.cancelInFlight === true ? { cancelInFlight: true } : {}),
      };
    }
    case 'sendMessage': {
      assertExactKeys(
        value,
        ['type', 'idempotencyKey', 'role', 'message', 'priority', 'idleBehavior', 'prepareBinding'],
        'Factory send message decision',

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Coerce config values to real booleans with === 'true' or Boolean(...) before assigning, but avoid passing null (use undefined to omit).
  2. Use undefined (field absent) rather than null when no cancel behavior is intended.
  3. Normalize YAML parsing with a schema (e.g. zod boolean) at config-load time.

Example fix

// before
cancelInFlight: config.cancelInFlight // 'true' (string from env/YAML)
// after
cancelInFlight: config.cancelInFlight === undefined ? undefined : config.cancelInFlight === true || config.cancelInFlight === 'true'
Defensive patterns

Strategy: type-guard

Validate before calling

function checkCancelInFlight(d) {
  if (d.cancelInFlight !== undefined && typeof d.cancelInFlight !== 'boolean') {
    throw new Error('cancelInFlight must be boolean or omitted');
  }
}

Type guard

function hasValidCancelFlag(d: { cancelInFlight?: unknown }): d is { cancelInFlight?: boolean } {
  const { cancelInFlight } = d;
  return cancelInFlight === undefined || typeof cancelInFlight === 'boolean';
}

Try / catch

try {
  commitDecision(decision);
} catch (e) {
  if (e instanceof FactoryRuleValidationError && /cancelInFlight must be a boolean/.test(e.message)) {
    const { cancelInFlight, ...rest } = decision;
    return commitDecision({ ...rest, cancelInFlight: cancelInFlight === true });
  }
  throw e;
}

Prevention

When it happens

Trigger: Emitting an invoke-skill decision where value.cancelInFlight is defined but not typeof 'boolean' — e.g. cancelInFlight: 'true' from parsed JSON/YAML config, cancelInFlight: 1, or cancelInFlight: null.

Common situations: Rules whose options come from YAML/JSON config or environment variables where booleans deserialize as strings; template interpolation producing numbers; API responses carrying 0/1 flags.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/1a785701de299ff3. Report an issue: GitHub.