MemberJunction / MemberJunction/MJ
BaseEntity.Validate() accepts an empty string for a NOT NULL string column, so a required field the form paints red still saves
- Dominant language
- TSQL
- Stars
- 29
- Forks
- 6
- Avg merge
- 2d 1h
- Merged PRs (30d)
- 323
Description
## Summary
`EntityField.Validate()` treats an empty string as satisfying a NOT NULL string column, so a required field that the form paints red as "required and empty" still saves — with `''` persisted in the column. The UI and the save disagree about the same value.
## Repro
MJ `next` @ `6ac6bcc5ca` (6.1.0-edge), Explorer, any entity with a NOT NULL nvarchar column that has no default. Used `MJ: AI Agent Request Types` → `Name` (nvarchar(200), `AllowsNull = 0`, no default).
1. Open an existing record, click **Edit**.
2. Clear `Name`. The field paints its underline red (`mj-forms-field--required-empty`) — the form clearly considers it invalid.
3. Click **Save**.
**Expected:** the save is refused with a field-named validation failure, the same way a `null` would be.
**Actual:** "Record saved successfully". The row now has `Name = ''` (`LEN(Name) = 0`, verified in SQL). Observed live on 2026-09-10 while validating #4355; the record was restored by hand.
## Root cause
`packages/MJCore/src/generic/baseEntity.ts`, `EntityField.Validate()`, nullability rung:
```ts
if (!ef.AllowsNull && (this.Value === null || this.Value === undefined)) {
```
Only `null` / `undefined` fail. `''` passes, and SQL Server accepts `''` in a NOT NULL column, so nothing downstream catches it either.
The form field takes the opposite view — `MjFormFieldComponent.IsRequiredEmpty`:
```ts
return val === null || val === undefined || val === '';
```
So the two rules that are supposed to describe the same thing ("this required field has no value") disagree exactly on `''`. The new section indicators from #4355 mirror the field rule, so they also flag `''` as invalid before save — consistent with the field, inconsistent with `Validate()` until this is settled.
## Proposed fix
Treat an empty (or whitespace-only) string as "missing" for `AllowsNull === false` **string** columns in `EntityField.Validate()`:
```ts
const missing = this.Value === null || this.Value === undefined
|| (ef.TSType === EntityFieldTSType.String && typeof this.Value === 'string' && this.Value.trim().length === 0);
if (!ef.AllowsNull && missing) { ... }
```
keeping the existing default-value / new-record carve-out as is. That matches what a user means by a required text field, and it is what the form already promises visually.
If there are entities where an empty string in a NOT NULL column is legitimately meaningful, the alternative is the other direction — stop `IsRequiredEmpty` from painting `''` red — but that seems the less useful contract. Either way the two should agree.
## Notes
- Not a regression: the nullability rung has read this way for a long time; #4355 just made the disagreement visible at the section level as well as the field level.
- Test to add alongside the fix: `packages/MJCore/src/__tests__/` — a NOT NULL string field set to `''` and to `' '` on an existing record fails `Validate()` with `Source` = the field name; a nullable string field set to `''` still passes.
Contributor guide
Research direction
Start in packages/MJCore/src/generic/baseEntity.ts at EntityField.Validate(), then inspect MjFormFieldComponent.IsRequiredEmpty to compare the existing required-field rules. Add tests under packages/MJCore/src/__tests__/ for empty and whitespace-only values on non-null string fields, while confirming nullable empty strings still pass. Done means validation reports the field name and blocks the save consistently with the form.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 88/100