{"record":{"id":"c00525441e451380","repo":"sipeed/picoclaw","slug":"enter-and-confirm-the-new-login-password","errorCode":null,"errorMessage":"Enter and confirm the new login password.","messagePattern":"Enter and confirm the new login password\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"web/frontend/src/components/config/config-page.tsx","lineNumber":302,"sourceCode":"    } catch (err) {\n      toast.error(\n        err instanceof Error\n          ? err.message\n          : t(\"pages.config.factory_reset_error\"),\n      )\n    } finally {\n      setShowFactoryResetDialog(false)\n    }\n  }\n\n  const handleSave = async () => {\n    try {\n      setSaving(true)\n      const password = launcherForm.dashboardPassword.trim()\n      const confirm = launcherForm.dashboardPasswordConfirm.trim()\n      if (launcherPasswordDirty) {\n        if (!password) {\n          throw new Error(t(\"pages.config.dashboard_password_required\"))\n        }\n        if (password !== confirm) {\n          throw new Error(t(\"pages.config.dashboard_password_mismatch\"))\n        }\n        if (Array.from(password).length < 8) {\n          throw new Error(t(\"pages.config.dashboard_password_min_length\"))\n        }\n      }\n\n      if (configDirty) {\n        const workspace = form.workspace.trim()\n        const dmScope = form.dmScope.trim()\n\n        if (!workspace) {\n          throw new Error(\"Workspace path is required.\")\n        }\n        if (!dmScope) {\n          throw new Error(\"Session scope is required.\")","sourceCodeStart":284,"sourceCodeEnd":320,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/web/frontend/src/components/config/config-page.tsx#L284-L320","documentation":"Validation error thrown in handleSave (web/frontend/src/components/config/config-page.tsx:302, message from i18n key pages.config.dashboard_password_required) when the dashboard password fields are dirty (launcherPasswordDirty) but the trimmed password is empty. It guards the subsequent POST /api/auth/setup call, which would otherwise send an empty password. Because dirty-flag based, it fires only after the user actually edited one of the two password inputs.","triggerScenarios":"Typing into the confirm box only, typing then deleting the password, or entering only whitespace; save then proceeds to fail before any network call.","commonSituations":"User believes password is optional and only fills confirm; browser autofill marks the field dirty then clears it; user pastes whitespace by accident.","solutions":["Enter a value of at least 8 characters into both the password and confirm fields","If you do not intend to change the password, make both fields identical to the baseline (empty and untouched) so the dirty flag clears","Re-open the page to reset dirty state, then save without touching password fields"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const password = launcherForm.dashboardPassword.trim()\nconst confirm = launcherForm.dashboardPasswordConfirm.trim()\nif (launcherPasswordDirty && (!password || !confirm)) {\n  setFieldError(\"Enter and confirm the new login password.\")\n  return // do not call save\n}","typeGuard":null,"tryCatchPattern":"try {\n  await handleSave()\n} catch (err) {\n  // validation errors are expected user input feedback, not bugs\n  if (err instanceof Error) setError(err.message)\n}","preventionTips":["Mark the confirm field required as soon as password is non-empty (and vice versa)","Disable the save button while a dirty password field is empty","Validate on blur so the user learns before hitting save"],"tags":["validation","frontend","password","config","i18n"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}