{"record":{"id":"3bf654d38755d71c","repo":"plotly/plotly.js","slug":"randstr-failed-uniqueness","errorCode":null,"errorMessage":"randstr failed uniqueness","messagePattern":"randstr failed uniqueness","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/lib/index.js","lineNumber":361,"sourceCode":"    }\n\n    var rem = digits - Math.floor(digits);\n\n    for (i = 0; i < Math.floor(digits); i++) {\n        x = Math.floor(Math.random() * base).toString(base);\n        res = x + res;\n    }\n\n    if (rem) {\n        b = Math.pow(base, rem);\n        x = Math.floor(Math.random() * b).toString(base);\n        res = x + res;\n    }\n\n    var parsed = parseInt(res, base);\n    if ((existing && existing[res]) || (parsed !== Infinity && parsed >= Math.pow(2, bits))) {\n        if (_recursion > 10) {\n            lib.warn('randstr failed uniqueness');\n            return res;\n        }\n        return randstr(existing, bits, base, (_recursion || 0) + 1);\n    } else return res;\n};\n\nlib.OptionControl = function (opt, optname) {\n    /*\n     * An environment to contain all option setters and\n     * getters that collectively modify opts.\n     *\n     * You can call up opts from any function in new object\n     * as this.optname || this.opt\n     *\n     * See FitOpts for example of usage\n     */\n    if (!opt) opt = {};\n    if (!optname) optname = 'opt';","sourceCodeStart":343,"sourceCodeEnd":379,"githubUrl":"https://github.com/plotly/plotly.js/blob/1d090e0b5ffb8d0fdf6e0e4ff51d3f3cf67f1314/src/lib/index.js#L343-L379","documentation":"plotly.js's randstr() generates pseudo-random string IDs by converting a random number to a given base. When a collision is detected (the string already exists in the `existing` map) or the value exceeds the requested bit range, it recurses to regenerate. If recursion exceeds 10 attempts, it gives up and logs 'randstr failed uniqueness', returning the duplicate/overflowing string anyway.","triggerScenarios":"Calling randstr(existing, bits, base) where the key space (2^bits at the given base) is too small relative to the number of existing keys, causing >10 consecutive collisions or overflow of the 2^bits limit.","commonSituations":"Generating many IDs with too few bits (e.g. bits too small for thousands of traces), seeding randstr with an `existing` map that already contains most of the small key space, or Math.random returning clustered values in constrained environments.","solutions":["Increase the `bits` argument to enlarge the key space (e.g. 64 instead of 24).","Reduce the number of pre-existing keys passed in the `existing` map.","Check the returned string for duplicates yourself after the warning and regenerate if needed.","Report upstream if it happens with default arguments on a small graph, as it indicates insufficient randomness."],"exampleFix":"// before\nvar id = randstr(existing, 12, 16); // only 4096 possible keys\n// after\nvar id = randstr(existing, 64, 16); // much larger key space","handlingStrategy":"validation","validationCode":"function checkIdSpace(existingCount, bits) {\n  const space = Math.pow(2, bits);\n  if (existingCount > space * 0.5) {\n    throw new Error('randstr key space too small; increase bits');\n  }\n}","typeGuard":"function isUnique(res, existing) { return typeof res === 'string' && !existing[res]; }","tryCatchPattern":null,"preventionTips":["Use a large bits value (e.g. 64) so collisions are vanishingly rare.","After generating an ID, verify it is not in the existing map before use.","Keep the existing map minimal - only pass IDs that must be avoided."],"tags":["id-generation","randomness","collision"],"backgroundTag":"random-id-collision","analyzedSha":"1d090e0b5ffb8d0fdf6e0e4ff51d3f3cf67f1314","analyzedAt":"2026-09-02T22:03:39.906Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}