{"record":{"id":"703f122485973801","repo":"NationalSecurityAgency/ghidra","slug":"lshexception-fatal-error","errorCode":null,"errorMessage":"LSHException fatal error","messagePattern":"LSHException fatal error","errorType":"exception","errorClass":"LSHException","httpStatus":null,"severity":"error","filePath":"Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/elastic/ElasticDatabase.java","lineNumber":1632,"sourceCode":"\t * @throws ElasticException for communication problems with the server\n\t * @throws LSHException if update differences cannot reconciled \n\t * @throws DatabaseNonFatalException for non-fatal updates that can't be executed\n\t */\n\tprivate boolean insertExe(DescriptionManager manager, ExecutableRecord exeRecord)\n\t\t\tthrows ElasticException, LSHException, DatabaseNonFatalException {\n\t\tRowKeyElastic eKey = updateKey(manager, exeRecord);\n\t\tString exeId = eKey.generateExeIdString();\n\n\t\tif (!insertExecutableRecord(exeRecord, exeId)) {\t// Try to insert the executable\n\t\t\tJsonObject exeObj = queryMd5ExeMatch(exeRecord.getMd5());\n\t\t\tif (exeObj != null) {\t\t// Try to retrieve the previous version\n\t\t\t\tExecutableRecord oldrec = makeExecutableRecordTemp(exeObj);\n\t\t\t\tint cmp = oldrec.compareMetadata(exeRecord);\n\t\t\t\tif (cmp != 0) {\n\t\t\t\t\tString fatalerror =\n\t\t\t\t\t\tFunctionDatabase.constructFatalError(cmp, exeRecord, oldrec);\n\t\t\t\t\tif (fatalerror != null) {\n\t\t\t\t\t\tthrow new LSHException(fatalerror);\n\t\t\t\t\t}\n\t\t\t\t\tthrow new DatabaseNonFatalException(\n\t\t\t\t\t\tFunctionDatabase.constructNonfatalError(cmp, exeRecord, oldrec));\n\t\t\t\t}\n\t\t\t\tthrow new DatabaseNonFatalException(\n\t\t\t\t\texeRecord.getNameExec() + \" is already ingested\");\n\t\t\t}\n\t\t\treturn false;\t\t// Indicate this executable already inserted\n\t\t}\n\t\tint newIds = 0;\n\t\tlong baseId = 0;\n\t\tIterator<FunctionDescription> iter = manager.listFunctions(exeRecord);\n\t\twhile (iter.hasNext()) {\t\t\t// Count the functions to insert for this executable\n\t\t\titer.next();\n\t\t\tnewIds += 1;\n\t\t}\n\t\tbaseId = allocateFunctionIndexSpace(newIds);\t\t// Allocated the ids we will need\n\t\titer = manager.listFunctions(exeRecord);","sourceCodeStart":1614,"sourceCodeEnd":1650,"githubUrl":"https://github.com/NationalSecurityAgency/ghidra/blob/d5f144c24d6bc53c9cbf4448c6d11143e7696206/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/elastic/ElasticDatabase.java#L1614-L1650","documentation":"When re-inserting an executable whose md5 already exists, compareMetadata returns a nonzero flags bit and constructFatalError is non-null for a 'fatal' bit: architecture (METADATA_ARCH), compiler (METADATA_COMP), library flag (METADATA_LIBR), or repository (METADATA_REPO). These are treated as incompatible and throw an LSHException (hard stop), because comparing functions across a different arch/compiler/repository is meaningless. The message is the constructFatalError text.","triggerScenarios":"Ingesting the same md5 with a different architecture/compiler string, a toggled library flag, or from a different repository override than the original ingest of that md5.","commonSituations":"Rebuilding a binary with a different toolchain and re-ingesting under the same md5; mixing a stripped vs non-stripped build; using --repo_override differently on the second ingest; cross-arch variants colliding on md5 (rare).","solutions":["If the change is intentional, drop the existing executable first (QueryDelete / dropexec) then re-ingest.","Align architecture/compiler/repository/library metadata with the originally ingested record before re-inserting.","Use a separate BSim database for the variant so its md5 does not collide."],"exampleFix":"// before\n// re-ingest same md5 built with a new toolchain -> LSHException\n// after\nif (db.hasExecutableForMd5(md5)) {\n    db.dropExecutable(md5);        // remove incompatible prior record\n}\ndb.insert(insertRequest);          // ingest fresh, compatible metadata","handlingStrategy":"validation","validationCode":"// Before inserting, compare metadata against any existing record for this md5.\nExecutableRecord existing = db.findExecutableByMd5(md5);\nif (existing != null) {\n    int fatalFlags = existing.compareMetadata(newRecord)\n        & (METADATA_ARCH | METADATA_COMP | METADATA_LIBR | METADATA_REPO);\n    if (fatalFlags != 0) {\n        throw new IllegalStateException(\"md5 collides with incompatible record; drop first\");\n    }\n}","typeGuard":"// True if re-inserting newRecord under an existing md5 would hit a fatal metadata conflict.\nboolean fatalConflict = Optional.ofNullable(db.findExecutableByMd5(md5))\n    .map(o -> (o.compareMetadata(newRecord) & FATAL_MASK) != 0)\n    .orElse(false);","tryCatchPattern":"try {\n    db.insert(req);\n} catch (LSHException e) {\n    if (e.getMessage().contains(\"already ingested with different\")) {\n        db.dropExecutable(md5); db.insert(req);\n    } else throw e;\n}","preventionTips":["Keep arch/compiler/repo metadata deterministic across rebuilds of the same binary.","Treat md5 + toolchain as the identity unit; vary the database when either changes."],"tags":["bsim","metadata","ingestion","duplicate"],"backgroundTag":null,"analyzedSha":"d5f144c24d6bc53c9cbf4448c6d11143e7696206","analyzedAt":"2026-08-14T01:00:57.564Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}