medusajs/medusa · warning

'zone' property at the ${j} index of the 'forms' property is

Error message

'zone' property at the ${j} index of the 'forms' property is not a string literal. The 'zone' property must be a string literal, e.g. 'general' or 'attributes'.

What it means

The plugin requires 'zone' to be a string literal because it reads the value at build time via AST parsing; expressions like a variable, template literal, or concatenation cannot be resolved. If zoneProperty.value is not a StringLiteral node, this warning is emitted and the form entry is skipped.

Source

Thrown at packages/admin/admin-vite-plugin/src/custom-fields/generate-custom-field-forms.ts:302

  formArray.elements.forEach((element, j) => {
    if (!isObjectExpression(element)) {
      return
    }

    const zoneProperty = element.properties.find(
      (p) => isObjectProperty(p) && isIdentifier(p.key, { name: "zone" })
    ) as ObjectProperty | undefined

    if (!zoneProperty) {
      logger.warn(
        `'zone' property is missing from the ${j} index of the 'forms' property. The 'zone' property is required to load a custom field form.`,
        { file }
      )
      return
    }

    if (!isStringLiteral(zoneProperty.value)) {
      logger.warn(
        `'zone' property at the ${j} index of the 'forms' property is not a string literal. The 'zone' property must be a string literal, e.g. 'general' or 'attributes'.`,
        { file }
      )
      return
    }

    const tabProperty = element.properties.find(
      (p) => isObjectProperty(p) && isIdentifier(p.key, { name: "tab" })
    ) as ObjectProperty | undefined

    let tab: string | undefined

    if (tabProperty) {
      if (!isStringLiteral(tabProperty.value)) {
        logger.warn(
          `'tab' property at the ${j} index of the 'forms' property is not a string literal. The 'tab' property must be a string literal, e.g. 'general' or 'attributes'.`,
          { file }
        )

View on GitHub (pinned to 5e06e544a2)

Solutions

  1. Inline the literal string: zone: 'general'
  2. Keep the literal directly in the config file rather than referencing a constant
  3. If multiple forms share zones, repeat the literal per entry

Example fix

// before
const ZONE = 'general'
forms: [{ zone: ZONE, fields: {...} }]
// after
forms: [{ zone: 'general', fields: {...} }]
Defensive patterns

Strategy: type-guard

Validate before calling

for (const [i, f] of forms.entries()) {
  if (typeof f.zone !== 'string') throw new Error(`forms[${i}].zone must be an inline string literal`)
}

Type guard

const isStringLiteral = (v: unknown): v is string => typeof v === 'string'

Prevention

When it happens

Trigger: zone: ZONE constant, zone: `general` (template literal), zone: 'gen'+'eral', or zone: getZone() in a form entry.

Common situations: Extracting zone names into shared constants/imports, using template literals out of habit, or generating configs dynamically.

Related errors


AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27). Data as JSON: /api/errors/6970a9d36671bd25. Report an issue: GitHub.