{"record":{"id":"4a3086579cb85176","repo":"gchq/CyberChef","slug":"error-err-tostring","errorCode":null,"errorMessage":"Error: ${err.toString()}","messagePattern":"Error: (.+?)","errorType":"exception","errorClass":"OperationError","httpStatus":null,"severity":"error","filePath":"src/core/operations/BcryptParse.mjs","lineNumber":43,"sourceCode":"        this.infoURL = \"https://wikipedia.org/wiki/Bcrypt\";\n        this.inputType = \"string\";\n        this.outputType = \"string\";\n        this.args = [];\n    }\n\n    /**\n     * @param {string} input\n     * @param {Object[]} args\n     * @returns {string}\n     */\n    async run(input, args) {\n        try {\n            return `Rounds: ${bcrypt.getRounds(input)}\nSalt: ${bcrypt.getSalt(input)}\nPassword hash: ${input.split(bcrypt.getSalt(input))[1]}\nFull hash: ${input}`;\n        } catch (err) {\n            throw new OperationError(\"Error: \" + err.toString());\n        }\n    }\n\n}\n\nexport default BcryptParse;\n","sourceCodeStart":25,"sourceCodeEnd":50,"githubUrl":"https://github.com/gchq/CyberChef/blob/4290ea753912378913b1f3f54e0fc5720afeda5d/src/core/operations/BcryptParse.mjs#L25-L50","documentation":"Generic catch-all thrown by BcryptParse.run when bcrypt.getRounds(input) or bcrypt.getSalt(input) throws. The operation extracts the cost factor and salt from a bcrypt hash string; if the input is not a well-formed bcrypt hash, these accessors throw and the error is rewrapped as 'Error: <original>'. The operation is read-only metadata extraction, so any failure indicates the input is not a parseable bcrypt hash.","triggerScenarios":"Input that is not a bcrypt hash (no $2a/$2b/$2y prefix, missing cost field, invalid base64 salt, or a plain password/different-algorithm hash).","commonSituations":"Pasting an MD5/SHA-256 hash expecting to 'parse' it; hash copied without the version/cost prefix; whitespace or newline padding around the hash.","solutions":["Provide a complete bcrypt hash string ($2a$/$2b$/$2y$ + cost + salt + hash).","Trim surrounding whitespace/newlines before parsing.","If you need to inspect a different algorithm's hash, use the corresponding analyser operation instead."],"exampleFix":"// before - plain SHA hash\nchef.bcryptParse(\"5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8\");\n\n// after - valid bcrypt hash\nchef.bcryptParse(\"$2b$12$abcdefghijklmnopqrstuuVXQ1Nh2yTLB07h2yTLB07h2yTLB07h\");","handlingStrategy":"validation","validationCode":"const BCRYPT_HASH_RE = /^\\$2[aby]\\$\\d{2}\\$[./A-Za-z0-9]{53}$/;\nfunction assertBcryptHash(hash) {\n  const h = String(hash).trim();\n  if (!BCRYPT_HASH_RE.test(h)) {\n    throw new Error(\"Not a valid bcrypt hash; cannot parse rounds/salt\");\n  }\n  return h;\n}\nassertBcryptHash(input);","typeGuard":"function isBcryptHash(s) {\n  return /^\\$2[aby]\\$\\d{2}\\$[./A-Za-z0-9]{53}$/.test(String(s).trim());\n}","tryCatchPattern":"try {\n  await chef.bcryptParse(input);\n} catch (e) {\n  if (/invalid|illegal|not a valid/i.test(e.message)) {\n    throw new Error(`Input is not a bcrypt hash: ${e.message}`);\n  }\n  throw e;\n}","preventionTips":["Only feed genuine bcrypt hashes to BcryptParse.","Trim whitespace/newlines before parsing.","Use the matching analyser for non-bcrypt hashes."],"tags":["crypto","bcrypt","password","input-validation"],"backgroundTag":null,"analyzedSha":"4290ea753912378913b1f3f54e0fc5720afeda5d","analyzedAt":"2026-08-13T06:05:50.210Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}