octobercms/october · error · SystemException

NestedForm must specify either a "form" property for the for

Error message

NestedForm must specify either a "form" property for the form fields

What it means

NestedFormField builds its nested fieldset solely from the `form:` key of the field config (the constructor only assigns fieldsetConfig when isset($config['form'])). validateConfig() requires a non-null fieldsetConfig, so a nestedform field without a `form:` block — nothing to render or extend into the model — throws a SystemException.

Source

Thrown at modules/tailor/contentfields/NestedFormField.php:40

    public $fieldsetConfig;

    /**
     * defineConfig will process the field configuration.
     */
    public function defineConfig(array $config)
    {
        if (isset($config['form'])) {
            $this->fieldsetConfig = (array) $config['form'];
        }
    }

    /**
     * validateConfig
     */
    public function validateConfig()
    {
        if ($this->fieldsetConfig === null) {
            throw new SystemException('NestedForm must specify either a "form" property for the form fields');
        }
    }

    /**
     * defineFormField will define how a field is displayed in a form.
     */
    public function defineFormField(FormElement $form, $context = null)
    {
        $form->addFormField($this->fieldName, $this->label)->defaultCreate()->useConfig($this->getCleanFormConfig());
    }

    /**
     * defineBatchListColumn
     */
    public function defineBatchListColumn(ListElement $list, $context = null)
    {
        $list->defineColumn($this->fieldName, $this->label);
    }

View on GitHub (pinned to b608633a7e)

Solutions

  1. Add a `form:` block containing a `fields:` map under the nestedform field.
  2. Check indentation so `form:` is a direct child of the field and actually contains fields.
  3. If you only need a single level of nested fields without form nesting, use a regular field group instead.

Example fix

# before
fields:
  address:
    label: Address
    type: nestedform

# after
fields:
  address:
    label: Address
    type: nestedform
    form:
      fields:
        street:
          type: text
        city:
          type: text
Defensive patterns

Strategy: validation

Validate before calling

$fieldConfig = $blueprintContent['fields'][$fieldName] ?? [];
if (($fieldConfig['type'] ?? '') === 'nestedform' && !isset($fieldConfig['form'])) {
    throw new InvalidArgumentException("nestedform field '{$fieldName}' requires a form: block.");
}

Type guard

function hasFormBlock(array $fieldConfig): bool
{
    return isset($fieldConfig['form']) && is_array($fieldConfig['form']);
}

Try / catch

try {
    // blueprint validation / tailor:refresh
} catch (\SystemException $e) {
    // add form: { fields: { ... } } under the nestedform field and revalidate
}

Prevention

When it happens

Trigger: A field `{type: nestedform}` is defined with no `form:` key, or `form:` is present but empty/unparsed so isset() fails or the value doesn't reach the constructor. Validation runs when Tailor compiles the blueprint fieldset.

Common situations: Expecting nestedform to work like a partial without config; YAML indentation placing `form` under the wrong level; a `form:` value that parses to null; fieldsets imported from other projects missing the form block.

Related errors


AI-assisted analysis of octobercms/october@b608633a7e (2026-08-21). Data as JSON: /api/errors/bff20dec19710a9e. Report an issue: GitHub.