tursodatabase/turso · critical · SystemExit
self-test failed: missing export was not reported
Error message
self-test failed: missing export was not reported
What it means
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.
Source
Thrown at bindings/dotnet/scripts/check_elf_dynsym_exports.py:40
path = Path(sys.argv[1])
needed = sys.argv[2:]
missing = missing_exports(path, needed)
if missing:
raise SystemExit(f"{path} is missing dynsym exports: {', '.join(missing)}")
def run_self_test() -> None:
import tempfile
symbol = "turso_sync_database_new"
with tempfile.NamedTemporaryFile(delete=False, suffix=".so") as handle:
handle.write(elf64_with_dynsym(symbol))
path = Path(handle.name)
try:
if missing_exports(path, [symbol]):
raise SystemExit("self-test failed: expected export was not found")
if not missing_exports(path, ["turso_sync_database_missing"]):
raise SystemExit("self-test failed: missing export was not reported")
finally:
path.unlink()
def missing_exports(path: Path, needed: list[str]) -> list[str]:
names = dynsym_names(path)
return [symbol for symbol in needed if symbol not in names]
def elf64_with_dynsym(symbol: str) -> bytes:
shstrtab = b"\0.shstrtab\0.dynstr\0.dynsym\0"
dynstr = b"\0" + symbol.encode("ascii") + b"\0"
dynsym = bytes(24) + struct.pack("<IBBHQQ", 1, 0x12, 0, 1, 0x1000, 8)
shstrtab_offset = 64
dynstr_offset = shstrtab_offset + len(shstrtab)
dynsym_offset = dynstr_offset + len(dynstr)
dynsym_pad = (8 - (dynsym_offset % 8)) % 8View on GitHub (pinned to 492c4a71cd)
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
Example fix
// before return [symbol for symbol in needed if symbol in names] # inverted // after return [symbol for symbol in needed if symbol not in names]
Defensive patterns
Strategy: try-catch
Validate before calling
result = subprocess.run(["check_elf_dynsym_exports.py", "--self-test"], check=True)
Try / catch
try:
subprocess.run(["check_elf_dynsym_exports.py", "--self-test"], check=True)
except subprocess.CalledProcessError:
# negative case broken: audit membership logic in missing_exports()
raise Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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).
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- self-test failed: expected export was not found
- pyturso's SQLAlchemy dialects require SQLAlchemy >= 2.0.45 (
- Cannot get raw connection from SQLAlchemy connection
- Expected turso.sync.ConnectionSync, got {type(dbapi_conn).__
- Unsupported value type: {type(value).__name__}
AI-assisted analysis of tursodatabase/turso@492c4a71cd (2026-09-13).
Data as JSON: /api/errors/1303cf93da3dfeb8.
Report an issue: GitHub.