{"record":{"id":"1303cf93da3dfeb8","repo":"tursodatabase/turso","slug":"self-test-failed-missing-export-was-not-reported","errorCode":null,"errorMessage":"self-test failed: missing export was not reported","messagePattern":"self-test failed: missing export was not reported","errorType":"console","errorClass":"SystemExit","httpStatus":null,"severity":"critical","filePath":"bindings/dotnet/scripts/check_elf_dynsym_exports.py","lineNumber":40,"sourceCode":"    path = Path(sys.argv[1])\n    needed = sys.argv[2:]\n    missing = missing_exports(path, needed)\n    if missing:\n        raise SystemExit(f\"{path} is missing dynsym exports: {', '.join(missing)}\")\n\n\ndef run_self_test() -> None:\n    import tempfile\n\n    symbol = \"turso_sync_database_new\"\n    with tempfile.NamedTemporaryFile(delete=False, suffix=\".so\") as handle:\n        handle.write(elf64_with_dynsym(symbol))\n        path = Path(handle.name)\n    try:\n        if missing_exports(path, [symbol]):\n            raise SystemExit(\"self-test failed: expected export was not found\")\n        if not missing_exports(path, [\"turso_sync_database_missing\"]):\n            raise SystemExit(\"self-test failed: missing export was not reported\")\n    finally:\n        path.unlink()\n\n\ndef missing_exports(path: Path, needed: list[str]) -> list[str]:\n    names = dynsym_names(path)\n    return [symbol for symbol in needed if symbol not in names]\n\n\ndef elf64_with_dynsym(symbol: str) -> bytes:\n    shstrtab = b\"\\0.shstrtab\\0.dynstr\\0.dynsym\\0\"\n    dynstr = b\"\\0\" + symbol.encode(\"ascii\") + b\"\\0\"\n    dynsym = bytes(24) + struct.pack(\"<IBBHQQ\", 1, 0x12, 0, 1, 0x1000, 8)\n\n    shstrtab_offset = 64\n    dynstr_offset = shstrtab_offset + len(shstrtab)\n    dynsym_offset = dynstr_offset + len(dynstr)\n    dynsym_pad = (8 - (dynsym_offset % 8)) % 8","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/tursodatabase/turso/blob/492c4a71cd7c2649e7df83da1471b74f4b1c7aa9/bindings/dotnet/scripts/check_elf_dynsym_exports.py#L22-L58","documentation":"The second half of the script's --self-test: it checks that a deliberately-absent symbol IS reported as missing. This error means missing_exports() returned an empty list for the fake symbol 'turso_sync_database_missing', i.e. the checker falsely claimed an export exists. The detection logic is over-matching and would silently pass broken libraries in CI.","triggerScenarios":"Running `check_elf_dynsym_exports.py --self-test` when `not missing_exports(path, [\"turso_sync_database_missing\"])` is False — the parser returned a symbol set that contains names it should not, or membership logic is inverted.","commonSituations":"A refactor of names_from_dynsym() or missing_exports() inverted a condition, the name decoding started emitting placeholder names, or someone changed the membership check from `symbol not in names` to something too permissive (e.g. prefix matching).","solutions":["Inspect missing_exports() — ensure it filters with `symbol not in names` exactly","Inspect names_from_dynsym() to confirm it only adds non-empty, actually-present dynsym names","Check that the synthetic ELF's dynstr/dynsym only contain the one intended symbol","Re-run --self-test after the fix to confirm both positive and negative cases pass"],"exampleFix":"// before\nreturn [symbol for symbol in needed if symbol in names]  # inverted\n// after\nreturn [symbol for symbol in needed if symbol not in names]","handlingStrategy":"try-catch","validationCode":"result = subprocess.run([\"check_elf_dynsym_exports.py\", \"--self-test\"], check=True)","typeGuard":null,"tryCatchPattern":"try:\n    subprocess.run([\"check_elf_dynsym_exports.py\", \"--self-test\"], check=True)\nexcept subprocess.CalledProcessError:\n    # negative case broken: audit membership logic in missing_exports()\n    raise","preventionTips":["Never weaken the `symbol not in names` membership check to prefix/partial matching","Keep the self-test as a merge gate for changes to names_from_dynsym()","Only add non-empty decoded names to the returned set"],"tags":["self-test","python","false-negative"],"backgroundTag":"internal-invariant-violation","analyzedSha":"492c4a71cd7c2649e7df83da1471b74f4b1c7aa9","analyzedAt":"2026-09-13T18:13:59.796Z","contentChangedAt":"2026-09-13T18:13:59.796Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}