{"record":{"id":"741fe12db099526b","repo":"passbolt/passbolt_api","slug":"please-delete-previous-settings-before-creating-again","errorCode":null,"errorMessage":"Please delete previous settings before creating again.","messagePattern":"Please delete previous settings before creating again\\.","errorType":"exception","errorClass":"Cake\\Http\\Exception\\BadRequestException","httpStatus":400,"severity":"warning","filePath":"plugins/PassboltEe/Scim/src/Service/ScimSetSettingsService.php","lineNumber":85,"sourceCode":"\n        // Using this approach to avoid checking for setting_id duplicates on update\n        $validate = $id ? 'update' : 'extended';\n        if (!$form->execute($data, ['validate' => $validate])) {\n            throw new FormValidationException(\n                __('Could not validate the SCIM settings.'),\n                $form\n            );\n        }\n\n        /** @var \\Passbolt\\Scim\\Model\\Table\\ScimSettingsTable $scimSettingsTable */\n        $scimSettingsTable = $this->fetchTable('Passbolt/Scim.ScimSettings');\n        /** @var \\Passbolt\\Scim\\Model\\Entity\\ScimSetting|null $current */\n        $current = $scimSettingsTable->find()->first();\n        if (!$current && $id) {\n            throw new NotFoundException(__('The SCIM plugin is disabled.'));\n        }\n        if (!$id && $current) {\n            throw new BadRequestException(__('Please delete previous settings before creating again.'));\n        }\n        if ($current && $current->id !== $id) {\n            throw new NotFoundException(__('The uuid in the url doesn\\'t match any known setting record.'));\n        }\n\n        $currentValue = [];\n        $isDummyToken = $rawSecretToken === self::SCIM_SECRET_TOKEN_DUMMY;\n        if ($current) {\n            $currentValue = $this->decryptSettings($current);\n            $form->set('setting_id', Hash::get($currentValue, 'setting_id'));\n            if (!$form->getData('secret_token') || $isDummyToken) {\n                $form->set('secret_token', Hash::get($currentValue, 'secret_token'));\n            }\n        }\n\n        $settingsData = $form->getData();\n        $isTokenRotated = $this->isTokenRotated($rawSecretToken, $currentValue);\n        if (!$current || $isTokenRotated) {","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltEe/Scim/src/Service/ScimSetSettingsService.php#L67-L103","documentation":"The SCIM plugin stores at most one settings row. saveSettings() enforces this: if no $id was supplied (i.e., a create operation) but a settings row already exists, it throws BadRequestException telling the caller to delete the previous settings first. This prevents duplicate/conflicting SCIM configurations.","triggerScenarios":"POST /scim-settings when scim_settings already contains a row — typically a second admin clicking 'save' on a create form, or an automation trying to re-provision settings that already exist.","commonSituations":"Setup scripts run twice without idempotency checks; UI state out of sync (form still in 'create' mode after settings were saved); multi-admin environments where another admin configured SCIM first.","solutions":["Check GET /scim-settings before creating; if settings exist, issue PUT /scim-settings/<existing-id> instead of POST.","Delete the existing settings (DELETE /scim-settings/<id>) if replacement is truly intended, then create anew.","Make provisioning scripts idempotent: fetch-then-update rather than always POST."],"exampleFix":"// before\nawait api.post('/scim-settings.json', payload);\n// after\nconst existing = await api.get('/scim-settings.json');\nif (existing.data) {\n  await api.put(`/scim-settings/${existing.data.id}.json`, payload);\n} else {\n  await api.post('/scim-settings.json', payload);\n}","handlingStrategy":"validation","validationCode":"const { data } = await api.get('/scim-settings.json');\nif (data) {\n  return api.put(`/scim-settings/${data.id}.json`, payload); // update, don't create\n}\nreturn api.post('/scim-settings.json', payload);","typeGuard":null,"tryCatchPattern":"try {\n  await api.post('/scim-settings.json', payload);\n} catch (e) {\n  if (e.response?.status === 400 && /delete previous/i.test(e.response.data?.message ?? '')) {\n    const { data: cur } = await api.get('/scim-settings.json');\n    await api.put(`/scim-settings/${cur.id}.json`, payload);\n  }\n}","preventionTips":["Make provisioning scripts idempotent: GET first, then update or create.","Refresh the admin UI after save so the form switches to update mode.","Remember the plugin is singleton-per-installation: one settings row only."],"tags":["scim","php","conflict","rest","singleton"],"backgroundTag":"resource-already-exists","analyzedSha":"31c1bbc10f32808a607fa9bd81891e898779c0bc","analyzedAt":"2026-09-17T00:04:38.960Z","contentChangedAt":"2026-09-17T00:04:38.960Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}