Repository navigation
Bug 1565 cfieldsView Save Lock On Validation Error
Bug fix — Issue #1565: cfieldsView saveCf() leaves saving=true locked when client-side validation fails — Save button dead until reload
Issue: #1565
Branch: fix/issue-1565
Status: FIXED & VERIFIED (2026-09-22)
In the modern Custom Fields screen (gui/templates/cfields/cfieldsView.html), if the
create/edit modal fails client-side validation (empty Label or Name), the Save button
stops working for the rest of the session — every further click is silently swallowed —
until the page is reloaded.
-
http://localhost:8082/gui/templates/cfields/cfieldsView.html?tproject_id=1&tplan_id=0(admin/admin). - Click
+ Create Custom Field, leave Label/Name empty, clickSave→ "Label and Name are required.". - Fill Label =
Repro CF, Name =repro_cf, clickSaveagain.
Actual (pre-fix): no request fires (network panel unchanged), page-scope saving
stays true. Expected: the second Save POSTs the create.

-
saveCf()acquires a re-entrancy lock BEFORE validation —cfieldsView.html:344if (saving) return;and:345saving = true;. - The validation branch —
cfieldsView.html:359-362:if (!data.label || !data.name) { $('#modalError').text(TLi18n.t('cf.validation.required')).show(); return; }
returns WITHOUT releasing the lock. - Every other exit path releases it: ajax
success(:375) anderror(:385) both dosaving = false;. The validation path is the only gap. - Consequence: the guard at
:344swallows every later Save click until a reload re-runsvar saving = false;(:155).
Regression source: commit 06aaadab7 (Refs #953, "create-and-assign to current
test project") introduced the saving guard and the resets only on the ajax handlers;
the pre-existing validation return was not retrofitted.
Minimal one-line change — release the lock on the validation-error path:
if (!data.label || !data.name) {
$('#modalError').text(TLi18n.t('cf.validation.required')).show();
saving = false;
return;
}Rationale: the lock only exists to prevent double-submit while an ajax call is in
flight; a client-side validation failure means nothing was sent, so the flag must be
cleared for the user to correct the fields and retry (same contract as the
server-error handler). Alternatives rejected: reordering validation before the lock
(larger control-flow diff), try/finally (overkill for one return path).
Blast radius / scope: the saving lock is used only in cfieldsView.html and
platformsAssign.html:341,347 (brace variant) — the latter acquires it only after its
no-op checks and always resets via .always(), so it is unaffected.
cfieldsAssignView.html:300 has the same validation branch but NO saving guard →
unaffected.
Browser regression matrix (admin, fresh DB) — 6/6 PASS (see tmp/TLU_Test_Cases.md
suite 1565):
- Empty validate → error shown AND
saving === false(pre-fixtrue). - Retry after validation failure →
POST /api/cfields/index.php [200], modal closes, row appears. - Validate → cancel → reopen → still unlocked.
- Edit path →
GET /{id}+PUT /{id} [200]+ refresh. - Server-error path (duplicate name) →
POST [400]«Custom field name already exists»,saving === false, retry possible. - Event Viewer: only
log_level=16audit rows (login, create, update); zero Error/Warning; no new JS console errors.

-
gui/templates/cfields/cfieldsView.html— 1 line added (saving = false;). -
tmp/TLU_Test_Cases.md— Regression — Issue #1565 suite (6/6 PASS). -
CHANGELOG— KEY BUGFIX entry. - Docs mirror + wiki + two screenshots.
Issue #1565 closed as fixed: one-line release of the saving lock on the validation
failure path; full regression matrix passes error-free.