mastra-ai/mastra · error

Invalid signal XML ${label}: ${name}

Error message

Invalid signal XML ${label}: ${name}

What it means

Signal tag names (and attribute names) are serialized into XML, so they must match XML's Name production: start with a letter or underscore, followed by letters, digits, underscore, dot, or hyphen. assertXmlName enforces this before emitting markup, because an invalid name would produce malformed/unparseable XML in the model stream.

Source

Thrown at packages/core/src/agent/signals.ts:237

        : signal.acceptedAt
          ? new Date(signal.acceptedAt)
          : undefined,
  };
}

function escapeXml(value: string): string {
  return value.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');
}

function escapeXmlAttribute(value: string): string {
  return escapeXml(value).replaceAll('"', '&quot;');
}

const XML_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_.-]*$/;

function assertXmlName(name: string, label: string): void {
  if (!XML_NAME_PATTERN.test(name)) {
    throw new Error(`Invalid signal XML ${label}: ${name}`);
  }
}

function signalAttributesToXml(attributes?: AgentSignalAttributes): string {
  if (!attributes) {
    return '';
  }

  const serialized = Object.entries(attributes)
    .filter((entry): entry is [string, string | number | boolean] => entry[1] !== null && entry[1] !== undefined)
    .map(([key, value]) => {
      assertXmlName(key, 'attribute name');
      return `${key}="${escapeXmlAttribute(String(value))}"`;
    })
    .join(' ');

  return serialized ? ` ${serialized}` : '';
}

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Rename the tagName to satisfy the pattern /^[A-Za-z_][A-Za-z0-9_.-]*$/, e.g. 'system-reminder', 'user_note'.
  2. Sanitize dynamic names before creating the signal: strip invalid characters and prefix with a letter/underscore if it starts with a digit.
  3. If the display label needs spaces/other chars, put it in an attribute or content, not the tag name.
  4. Pre-validate with the same regex before calling createSignal when names come from user input.

Example fix

// before
createSignal({ type: 'reactive', tagName: 'my reminder!', contents });
// after
createSignal({ type: 'reactive', tagName: 'my-reminder', contents });
Defensive patterns

Strategy: validation

Validate before calling

const XML_NAME = /^[A-Za-z_][A-Za-z0-9_.-]*$/;
if (!XML_NAME.test(tagName)) throw new TypeError(`tagName ${tagName} is not a valid XML name`);

Type guard

function isValidXmlName(name: string): boolean {
  return /^[A-Za-z_][A-Za-z0-9_.-]*$/.test(name);
}

Try / catch

try {
  const signal = createSignal({ type: 'reactive', tagName, contents });
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Invalid signal XML')) {
    signal = createSignal({ type: 'reactive', tagName: sanitizeXmlName(tagName), contents });
  } else throw e;
}

Prevention

When it happens

Trigger: Creating a signal whose `tagName` (or attribute name, labeled by `label`) contains spaces, digits at the start, special characters like '<', ':', '/', or is empty — e.g. tagName 'my reminder' or '1-reminder'.

Common situations: Deriving tagName dynamically from user input or file/event names that contain spaces or slashes; using a camelCase tagName with invalid leading characters; forgetting to sanitize IDs used as tag names.

Related errors


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