Skip to content

Fix validation error for block inputs without validation annotations - #6215

Draft
VPS-thodax wants to merge 1 commit into
mainfrom
claude/com-3136-3jk0np
Draft

Fix validation error for block inputs without validation annotations#6215
VPS-thodax wants to merge 1 commit into
mainfrom
claude/com-3136-3jk0np

Conversation

@VPS-thodax

@VPS-thodax VPS-thodax commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Problem

A document cannot be saved as soon as it contains a block whose input class has no class-validator annotation at all — for instance a block without fields. Saving fails with:

an unknown value was passed to the validate function

Both rich text variants are affected, as they validate their link and child blocks with a direct validate() call: createRichTextBlock (DraftJS) and createTipTapRichTextBlock (link marks and child blocks).

Cause

class-validator enables forbidUnknownValues by default since v0.14. A class without any validation metadata is treated as an unknown value and rejected — even though there is nothing to validate. BlockInput carries no validation metadata itself, so a block input without own decorators has none at all.

Fix

BlockInput provides a property that carries validation metadata, so every block input has metadata regardless of its own fields. This works for every validation path without applications having to adapt their ValidationPipe.

The property is never set: it doesn't show up in toPlain(), in the block meta, or in the saved block data.

Decisions

  • Give BlockInput validation metadata instead of passing forbidUnknownValues: false to the validate() calls. Setting the option only fixes the call sites in the core, so any application validating a block input itself would run into the same error again. It also matches the existing precedent in Fix validation error caused by EmptyDamScope when uploading a file #3620, which gave EmptyDamScope an annotated dummy field for the same reason.
  • The property is annotated with @Equals(undefined) rather than @Allow(). An annotated property is whitelisted, so with @Allow() a client could send thisBlockInputNeedsValidationMetadata____ on any block and the value would end up in the saved block data — today forbidNonWhitelisted rejects unknown properties, and that stays intact.
  • The property is public. As private/protected, declaration emit fails with TS4094 for the anonymous input classes returned by the block factories (e.g. createBlocksBlock).

Similar workarounds in the codebase

A dummy property named …____ is the established way to satisfy a framework that rejects a class without members. It exists today for two reasons:

Unlike all of them, the property added here is not part of a GraphQL type, so it is annotated as unsettable instead of nullable.

Verification

block-input-validation.test.ts covers a block input without any annotation:

  • validated directly
  • as link block of a DraftJS rich text block
  • as link block of a TipTap rich text block
  • as child block of a TipTap rich text block
  • nested in a list block passed through a ValidationPipe
  • rejected when a client sends the property that provides the metadata

All of them fail without the change, except the ValidationPipe one — NestJS' ValidationPipe defaults forbidUnknownValues to false itself, so that path was never broken. It is covered nonetheless, as applications may configure the option themselves.

block-meta.json was regenerated in cms-api and in Demo and is unchanged.

Manual verification in the Demo admin

Reproduced and verified in the running Demo admin, as the reported case comes from an application. The Demo has no annotation-free link block, so a temporary one was added to LinkBlock for the test — it is not part of this PR.

Inserting a link of that type in a Rich Text block, then saving the page:

  • with the fix — the page saves, and the link is still there after reloading
  • with the fix reverted — saving fails with the admin's "Validation failed" error dialog, savePage returns BAD_REQUEST

The same applies to a link in a Rich Text (TipTap) block.

Adding a Contact Form block — which is annotation-free too, but sits in the page content and is therefore validated by the application's ValidationPipe — saves in both cases, which confirms that path was never broken.

Further information

…otations

class-validator rejects classes without any validation metadata since v0.14,
where forbidUnknownValues is enabled by default. Block inputs that don't
validate a single field - for instance a block without fields - therefore
always failed to validate, which made documents containing such a block
unsaveable. Registering validation metadata on BlockInput fixes this for
every validation path without applications having to adapt their
ValidationPipe.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TZkkSZg8EAHmU3NL63GsY8
@VPS-thodax VPS-thodax self-assigned this Aug 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants