{"record":{"id":"ae37e60580934050","repo":"gildas-lormeau/SingleFile","slug":"duplicate-profile-name","errorCode":null,"errorMessage":"Duplicate profile name","messagePattern":"Duplicate profile name","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"src/core/bg/config.js","lineNumber":545,"sourceCode":"\t\t\tObject.keys(syncConfig)\n\t\t\t\t.filter(keyName => keyName.startsWith(PROFILE_NAME_PREFIX))\n\t\t\t\t.forEach(keyName => profiles[keyName] = syncConfig[keyName]);\n\t\t\tawait browser.storage.local.set(profiles);\n\t\t}\n\t\tconfigStorage = browser.storage.local;\n\t\tawait upgrade();\n\t\treturn {};\n\t}\n\tif (message.method.endsWith(\".isSync\")) {\n\t\treturn { sync: (await browser.storage.local.get()).sync };\n\t}\n\treturn {};\n}\n\nasync function createProfile(profileName, fromProfileName) {\n\tconst profileNames = await getProfileNames();\n\tif (profileNames.includes(profileName)) {\n\t\tthrow new Error(\"Duplicate profile name\");\n\t}\n\tconst profileFrom = await getProfile(fromProfileName);\n\tconst profile = JSON.parse(JSON.stringify(profileFrom));\n\tprofile.customShortcut = null;\n\tawait setProfile(profileName, profile);\n}\n\nasync function getProfiles() {\n\tawait pendingUpgradePromise;\n\tconst profileKeyNames = await getProfileKeyNames();\n\tconst profiles = await configStorage.get(profileKeyNames);\n\tconst result = {};\n\tObject.keys(profiles).forEach(profileName => result[profileName.substring(PROFILE_NAME_PREFIX.length)] = profiles[profileName]);\n\treturn result;\n}\n\nasync function getOptions(url, autoSave) {\n\tawait pendingUpgradePromise;","sourceCodeStart":527,"sourceCodeEnd":563,"githubUrl":"https://github.com/gildas-lormeau/SingleFile/blob/517fb7c5cf2096d89933b747e862d8ecf616a9f9/src/core/bg/config.js#L527-L563","documentation":"createProfile validates that the requested profile name is unique across existing profiles before copying the source profile. If a profile with that name already exists, it refuses to overwrite silently and throws.","triggerScenarios":"createProfile(name, fromName) called with a name already present in getProfileNames() — e.g. duplicate UI submissions, replayed messages via onMessage, or programmatic creation without checking names.","commonSituations":"Double-clicking a create button in the options UI; importing settings that already contain the profile; scripts generating non-unique names from timestamps or counters that collide.","solutions":["Generate a unique name first (check getProfileNames() or append a suffix)","Catch the error and prompt the user for a different name","If overwrite is intended, use updateProfile on the existing profile instead of createProfile","Debounce/idempotent-guard the UI action that triggers creation"],"exampleFix":"// before\nawait config.createProfile('work', 'default');\n// after\nconst names = await config.getProfileNames();\nconst name = names.includes('work') ? 'work-2' : 'work';\nawait config.createProfile(name, 'default');","handlingStrategy":"validation","validationCode":"const names = await config.getProfileNames();\nif (names.includes(newName)) throw new Error('choose another name');","typeGuard":"const nameIsFree = async (n) => !(await config.getProfileNames()).includes(n);","tryCatchPattern":"try { await createProfile(name, from); } catch (e) { if (e.message === 'Duplicate profile name') name = await suggestUniqueName(name); else throw e; }","preventionTips":["Always check getProfileNames() before creating","Debounce create buttons to prevent double submissions","Auto-suffix names until unique in tooling","Use updateProfile when overwrite is intended"],"tags":["configuration","profile","duplicate"],"backgroundTag":"duplicate-profile-name","analyzedSha":"517fb7c5cf2096d89933b747e862d8ecf616a9f9","analyzedAt":"2026-09-01T10:05:25.770Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}