decompiler CLI
July 25, 2026 · View on GitHub
The decompiler command is a thin, LLM-friendly client over DecLib. You load a
binary once (which spawns a headless decompiler server in the background) and
then run quick inspection or mutation commands against it. Multiple binaries
and backends can be loaded at the same time; each server is identified by a
short ID.
This document is for humans; the short reference version used by LLM agents
lives at declib/skills/decompiler/SKILL.md
and can be installed with decompiler install-skill.
Table of contents
- Install & setup
- Quick start
- How it works
- Subcommand reference
- Server selection (
--id,--binary,--backend) - JSON output (
--json,--raw) - Exit codes
- Running multiple binaries at once
- Address formats
- Library-level API
- Troubleshooting
Install & setup
pip install declib
# Register DecLib plugins into every detected decompiler.
declib --install
# Or point the installer at one specific decompiler:
declib --single-decompiler-install binja "/Applications/Binary Ninja.app"
After pip install declib, two entry points are available:
declib— the existing management CLI (install plugins, run the server, etc.)decompiler— the new LLM-facing CLI documented here.
Pick a backend you have available:
- angr — pure Python, always available. Good for end-to-end testing and small/medium binaries.
- ghidra — requires
GHIDRA_INSTALL_DIRand uses PyGhidra. - binja — requires a Binary Ninja license.
- ida — requires IDA Pro.
- jadx — optional; requires the official JADX 1.5.6+ distribution and
Java 17+. DecLib finds it via
JADX_HOMEorjadxonPATH. Check setup withdecompiler backend status jadx --json. It uses stable JVM/Dex references and has dedicatedclass,method,field,resource, andmanifestcommands; see the JADX backend guide.
Quick start
# 1. Load a binary. The first call spawns a detached headless server.
decompiler load ./fauxware --backend angr
# id: 3308b81cf8 …
# 2. Poke around.
decompiler list_functions # enumerate every function first
decompiler decompile main # by name
decompiler disassemble 0x40071d # by absolute address
decompiler xref_to authenticate # every code+data reference
decompiler get_callers authenticate # call-sites only (subset of xref_to)
decompiler xref_from main # what main calls
decompiler list_strings --filter 'pass|key' # regex-filtered strings
# 3. Mutate the database.
decompiler rename func sub_400662 trampoline
decompiler rename var v2 auth_result --function main
# 4. Tear it down when you're done.
decompiler stop --all
How it works
┌─────────────┐ spawns ┌─────────────────────────┐
│ decompiler │ ────────────────▶ │ declib --server (headless│
│ CLI │ (first load) │ decompiler + AF_UNIX │
│ │ │ socket) │
│ │ ◀─────────────────│ │
└─────────────┘ every command └─────────────────────────┘
│
▼
~/.local/state/declib/servers/<id>.json ← the shared registry
Each running server writes a small JSON descriptor (id, socket_path,
binary_path, binary_hash, backend, pid, started_at) into a shared
registry directory. The CLI reads the registry to figure out which server to
talk to. Stale records (server exited, socket missing) are pruned on read.
Run decompiler list --show-registry to print just the path.
Every subcommand except load, list, and install-skill accepts
--id, --binary, and --backend to pick which server to target when you
have more than one running.
Subcommand reference
load
Load a binary, starting a headless server if one isn't already running for it.
decompiler load <binary> [--backend {angr,ghidra,binja,ida,jadx}]
[--id SERVER_ID]
[--force | --replace]
[--timeout SECONDS]
[--project-dir PATH]
[--json]
--backend(default:angr) — which decompiler to use.--id— explicit server ID; otherwise one is auto-generated.--force— start an additional server even if one already matches this(binary, backend). Keeps the old server alive.--replace— stop any existing server for this(binary, backend)first, then start a fresh one. Use this when you want to re-analyze from scratch.--timeout SECONDS(default: 300) — maximum time to wait for the backend to register. Slow Ghidra or IDA analyses may need a larger value; use a smaller value in automation when a strict task timebox matters.--project-dir PATH— where to keep the backend's project/database files (Ghidra project, IDA.id*/.til, etc.). Default: a per-binary directory under the user cache (<platformdirs cache>/declib/projects/<binary>-<hash>/), so analysis artifacts don't pollute the binary's directory. Pass--project-dir ""to disable the cache dir and let the backend drop files alongside the binary (legacy behavior).
For a newly started server, output includes id, socket_path, binary_path,
backend, project_dir, log_path, and status. status is either
started or already_loaded (the latter describes an existing server and
does not add a new log_path).
Detached server stdout and stderr are written to log_path. If the child
exits before registering, load fails immediately instead of waiting for the
full timeout and prints the final 8 KiB of that log. A timeout reports the same
path and bounded tail, which usually reveals a missing dependency, license
failure, backend exception, or stale project problem.
list
Show all running decompiler servers.
decompiler list [--show-registry] [--json]
Text output:
ID BACKEND PID BINARY
3308b81cf8 angr 57613 /…/fauxware
9d77ab8fd4 angr 57786 /…/posix_syscall
(registry: /Users/me/Library/Application Support/declib/servers)
--show-registry— print the registry directory and exit (useful for scripting manual cleanup).--jsonemits{"registry_dir": "...", "servers": [...]}.
batch
Execute several structured CLI operations over one persistent server connection. This avoids a new Python process, registry lookup, and socket handshake for every operation.
decompiler batch --file operations.jsonl [--id ID | --binary PATH | --backend BACKEND] [--stop-on-error] [--json]
decompiler batch --stdin [--id ID | --binary PATH | --backend BACKEND] [--stop-on-error] [--json]
The input is JSON Lines: one object with a unique optional id and a CLI
argument array per line.
{"id":"functions","argv":["list_functions","--filter","main|auth"]}
{"id":"main","argv":["decompile","main"]}
{"id":"header","argv":["read_memory","0","4","--format","hex"]}
Choose the server on the outer batch command. Operations inherit that
target and must not contain their own --id, --binary, or --backend.
Server lifecycle commands (load, list, stop, sync, and nested
batch) and unstructured --raw output are rejected inside a batch; normal
inspection and mutation commands, including save, are supported.
By default, output is compact JSONL: one result per completed operation and a
final summary line. --json emits one pretty JSON envelope with results
and summary. Each result includes the operation ID, command, exit status,
duration, and parsed JSON result or error. Processing continues after an
error unless --stop-on-error is set, and the batch exits nonzero if any
completed operation failed.
stop
Stop one or all servers.
decompiler stop [--id SERVER_ID] [--binary PATH] [--all] [--save | --discard] [--json]
You must pass one of --id, --binary, or --all. By default the backend's
native teardown applies (IDA discards, Ghidra persists). Pass --save to flush
analysis to disk first (durable across a reload), or --discard to explicitly
drop unsaved edits. --save and --discard are mutually exclusive.
save
Persist the backend's analysis to disk so renames, retypes, and comments
survive a stop + reload of the same --project-dir.
decompiler save [--path PATH] [--id ID] [--binary PATH] [--backend BACKEND] [--json]
Each backend writes its native artifact: IDA a .i64/.idb, Ghidra its
project, Binary Ninja a .bndb. angr is purely in-memory and has nothing to
persist — save exits 2 (not implemented) there. On reload, DecLib reopens
the saved database (IDA does so without re-analysis), so prior work is intact.
decompiler load ./fauxware --backend ida --project-dir ./proj
decompiler rename func authenticate auth_check
decompiler save # {"saved": true, "path": null}
decompiler stop --id <id>
decompiler load ./fauxware --backend ida --project-dir ./proj # reopens saved .i64
decompiler list_functions --filter auth_check # rename survived
list_functions
Enumerate every function in the loaded binary. This is usually the first thing you want on a new (possibly stripped) binary.
decompiler list_functions [--filter REGEX] [--id ID] [--binary PATH] [--backend BACKEND] [--json]
Text output:
ADDR SIZE NAME
0x540 6 __libc_start_main
0x71d 184 main
0x664 184 authenticate
...
JSON output is a list of {"addr": int, "size": int, "name": str, "addr_hex": str}.
decompile
Decompile a function to pseudocode.
decompiler decompile <target> [--raw | --map-lines --json] \
[--lines START:END ... | --grep REGEX ... [--context N] [--ignore-case]] \
[--max-chars N] [--output PATH [--force-output]] \
[--id ID] [--binary PATH] [--backend BACKEND]
<target> is a function name or address (hex/decimal, lifted or absolute —
see Address formats).
--raw— print the decompilation text directly, skipping all wrapping. Useful at a terminal when--json's escaped\ns are unreadable.--map-lines --json— ask the backend to map pseudocode lines to the corresponding lifted instruction addresses. The JSON response adds a deterministicline_maplist. Each record hasline, sorted integeraddrs, and matchingaddrs_hexvalues. A line may map to several instructions, and some pseudocode lines may have no mapping.--lines START:END— return only a 1-based inclusive pseudocode line range. Repeat it for multiple ranges;START:and:ENDare accepted.--grep REGEX— return lines matching a regular expression. Repeat it to match any expression.--context Nadds surrounding lines and--ignore-casechanges all patterns to case-insensitive matching.--grepand--linesare mutually exclusive.--max-chars N— cap the selected pseudocode at exactlyNcharacters. Metadata reports whether truncation occurred.--output PATH— write the selected pseudocode to a UTF-8 file and return only its absolute path, byte count, and SHA-256 digest instead of sending the body to stdout. Existing files are protected unless--force-outputis supplied.
Default text output is the decompilation. JSON output includes addr,
addr_hex, decompiler, and text, plus line_map when requested.
When any shaping option is used, JSON also includes total_chars,
total_lines, selected_chars, output_chars, output_lines,
source_ranges, matched_lines, truncated, and bounded. Line mappings
are restricted to selected source lines. --output replaces text with an
output metadata object, keeping large pseudocode out of agent context.
{
"addr": 1821,
"addr_hex": "0x71d",
"decompiler": "ida",
"text": "int main(...) { ... }",
"line_map": [
{"line": 4, "addrs": [1849], "addrs_hex": ["0x739"]}
]
}
Examples for a very large function:
# Inspect a known source region without returning the other 10,000 lines.
decompiler decompile processClientInput --lines 1450:1700 --json
# Find two concepts and include eight lines of context around every match.
decompiler decompile processClientInput \
--grep 'firmware|readlog' --context 8 --ignore-case --json
# Materialize once for later local rg/sed work; stdout contains metadata only.
decompiler decompile processClientInput --output /tmp/processClientInput.c --json
Error messages distinguish three failure modes:
- target not found — function name/address doesn't resolve.
- not a function start — address resolves, but isn't a function boundary. Exit 1.
- decompiler engine failed — address is a known function start, but the backend gave up. Exit 1.
disassemble
Disassemble a function to text assembly.
decompiler disassemble <target> [--raw] [--id ID] [--binary PATH] [--backend BACKEND] [--json]
Same error semantics and --raw flag as decompile.
xref_to
Every reference to target — code AND data.
decompiler xref_to <target> [--decompile] [--id ID] [--binary PATH] [--backend BACKEND] [--json]
<target> can be:
- a function name or address — resolves to function xrefs (who calls this function),
- a raw address that isn't a function start — resolves via the backend's address-level reference table (useful for globals, jump table entries, etc.),
- a string literal — looked up via
list_stringsfirst, then queried as a raw-address xref. Great for "who reads this constant?".
Each row has a kind field (Function, GlobalVariable, …) so you can
tell code refs from data refs. The JSON payload also carries
target_kind (function, address, or string) so callers can tell
which resolution path fired.
--decompile— ask the backend to decompile first. On Ghidra this surfaces additional references (e.g. globals pulled in through the HighFunction's global symbol map).
When you want only call-sites, reach for get_callers instead.
xref_from
Functions that target calls (its callees).
decompiler xref_from <target> [--id ID] [--binary PATH] [--backend BACKEND] [--json]
Implementation note: this prefers the backend's call-graph. If the
call-graph is unavailable it falls back to scanning the function's
disassembly for call 0x… instructions.
rename
Rename a function or a local variable.
# Rename a function.
decompiler rename func <old_name_or_addr> <new_name> [--id ID] [--json]
# Rename a local variable inside a function.
decompiler rename var <old_var_name> <new_var_name> --function <func_name_or_addr> [--id ID] [--json]
The CLI exits 1 if the rename didn't actually change anything (the
response's success field is authoritative).
comment
Get, set, append, delete, or list comments (annotations), keyed by address.
decompiler comment set <addr> <text> [--decompiled] [--id ID] [--json]
decompiler comment append <addr> <text> [--decompiled] [--id ID] [--json]
decompiler comment get <addr> [--id ID] [--json]
decompiler comment delete <addr> [--id ID] [--json]
decompiler comment list [--filter REGEX] [--id ID] [--json]
--decompiled attaches the comment to the decompiler/pseudocode view instead
of the disassembly. set replaces any existing comment; append concatenates.
get exits 1 when there is no comment at the address.
decompiler comment set 0x71d "parses argv, calls authenticate"
decompiler comment append 0x71d "SOSNEAKY is the backdoor password"
decompiler comment get 0x71d
# {"addr": 1821, "decompiled": false, "comment": "parses argv, calls authenticate\nSOSNEAKY is the backdoor password", "addr_hex": "0x71d"}
decompiler comment list --filter backdoor --json
Backend notes: IDA accepts comments only at addresses inside a function;
Ghidra and Binary Ninja are more permissive. angr implements comment writes
but not reads/enumeration, so get/list return nothing there. Use save
(or stop --save) to persist comments across a reload.
global
List, read, rename, or retype global variables.
decompiler global list [--filter REGEX] [--id ID] [--json]
decompiler global get <addr> [--id ID] [--json]
decompiler global rename <addr> <new_name> [--id ID] [--json]
decompiler global retype <addr> <new_type> [--id ID] [--json]
decompiler global list --filter 'key|flag'
# ADDR SIZE TYPE NAME
# 0x4040 8 char * g_key
decompiler global rename 0x4040 g_secret_key
decompiler global retype 0x4040 "char[32]"
rename works everywhere globals are enumerated; retype is fully supported
on IDA and Binary Ninja, best-effort on Ghidra. angr has no global-variable
store, so its global commands return nothing.
signature
Get or set a function's full signature (return type + argument types/names).
decompiler signature get <func> [--id ID] [--json]
decompiler signature set <func> "<C prototype>" [--id ID] [--json]
decompiler signature get main
# int main(int argc, char **argv)
decompiler signature set main "int main(int argc, char **argv, char **envp)"
IDA applies the whole prototype atomically via SetType (it can also change the
parameter count). Ghidra and Binary Ninja retype/rename the parameters they
already recognize. angr sets argument names but not types.
list_strings
List strings the decompiler's own string detector has identified in the binary.
decompiler list_strings [--filter REGEX]
[--min-length N]
[--id ID] [--binary PATH] [--backend BACKEND] [--json]
--filter REGEX— only return strings matching the regex.--min-length N— drop strings shorter than N characters (default 4).
Text output is 0x<addr>\t<string> per line. JSON output is a list of
{"addr", "addr_hex", "string"} entries.
Fidelity caveat. This command only returns what the decompiler
itself surfaced — it does not second-guess the backend or supplement with
a file-level scan. Backend string detection quality varies
(angr < ghidra < ida); angr in particular misses most of .rodata.
If the output looks thin, cross-check with an external tool before
concluding a string isn't in the binary:
strings -a -n 4 ./target # classic strings(1)
rabin2 -z ./target # radare2, structured output
readelf -p .rodata ./target # ELF-specific, per section
Once you've located a string that way you can feed its address back into
the CLI via decompiler xref_to 0x... or decompiler decompile 0x....
get_callers
Functions that contain a call to target — a strict subset of xref_to.
decompiler get_callers <target> [--id ID] [--binary PATH] [--backend BACKEND] [--json]
Unlike xref_to, this never returns globals or other data refs. Rows are
always of kind Function.
eval / exec (UNSAFE backend scripting)
An escape hatch for backend-specific work the abstracted API doesn't cover. This executes arbitrary Python inside the backend process — not portable, not sandboxed.
decompiler eval "<expression>" [--id ID] [--json]
decompiler exec "<code>" [--id ID] [--json]
decompiler exec --file <path.py> [--id ID] [--json]
deci is the live DecompilerInterface; the backend's own API is reachable
through it (idaapi importable, deci.flat_api on Ghidra, deci.project on
angr, deci.bv on Binary Ninja). eval returns the expression's repr;
exec captures stdout and the value of a variable named result. Errors print
a traceback and exit non-zero.
decompiler eval "deci.name"
decompiler exec "print(hex(deci.binary_base_addr))"
decompiler exec --file ./analysis.py
patch
Apply, inspect, list, or revert byte patches.
decompiler patch set <addr> <hex> [--id ID] [--json]
decompiler patch get <addr> [--id ID] [--json]
decompiler patch delete <addr> [--id ID] [--json]
decompiler patch list [--id ID] [--json]
decompiler patch set 0x401200 "9090" # NOP two bytes
decompiler patch list
decompiler patch delete 0x401200 # revert (IDA)
patch set is implemented on IDA, Ghidra, and Binary Ninja; angr has no
user-patch store. Patch tracking (get/list/delete/revert) is IDA-only for
now — other backends apply the patch but don't record it. Use save to persist.
define and undefine
Repair analysis: create functions, disassemble code, define data, or clear a region back to undefined bytes.
decompiler define function <addr> [--id ID] [--json]
decompiler define code <addr> [--id ID] [--json]
decompiler define data <addr> [--type C_TYPE | --size N] [--json]
decompiler undefine <addr> [--size N] [--json]
decompiler define function 0x401200 # create a missed function
decompiler define data 0x4040 --type int
decompiler undefine 0x401200 --size 32 # removes a function starting here
Supported on IDA, Ghidra, and Binary Ninja. angr has no define/undefine
primitives and returns exit 2. A realistic repair is undefine →
define code → define function.
search and imports
Find byte/string/instruction patterns and enumerate imported symbols.
decompiler search bytes <hex> [--max N] [--id ID] [--json]
decompiler search string <text> [--encoding ENC] [--max N] [--json]
decompiler search instruction <regex> [--max N] [--json]
decompiler imports [--filter REGEX] [--id ID] [--json]
decompiler search bytes "7f454c46" # -> 0x0 (ELF magic at the base)
decompiler search string "SOSNEAKY"
decompiler search instruction "call.*authenticate"
decompiler imports --filter 'puts|read'
search bytes/string use each backend's native memory search (IDA, Ghidra,
Binary Ninja); angr lacks one and returns exit 2. search instruction is
client-side (greps disassembly), so it works everywhere but disassembles as it
goes — keep --max modest. imports is implemented on IDA, angr, and Binary
Ninja; Ghidra returns not implemented.
read (typed) and address semantics
read_memory returns raw bytes; read decodes them. Decoding happens
client-side over read_memory, so all three subcommands work on every backend.
decompiler read int <addr> [--size N] [--signed] [--endian little|big] [--json]
decompiler read string <addr> [--max-len N] [--encoding ENC] [--json]
decompiler read struct <addr> <struct-name> [--endian little|big] [--json]
decompiler read int 0x4040 --size 4 --endian big # -> {"value": 2130640198, ...}
decompiler read string 0x8e0 # -> "Welcome to ..."
decompiler create-type "struct Point { int x; int y; }"
decompiler read struct 0x4050 Point
# struct Point @ 0x4050 (size 8):
# +0x0 x int = 5 (05000000)
# +0x4 y int = 10 (0a000000)
Address forms. Every address argument (here and in read_memory, comment,
global, xref_*, ...) accepts a lifted offset (0x2320), an absolute
loaded address (0x402320), or decimal. The CLI rebases absolute addresses
to the backend's lifted form, so both refer to the same byte instead of the
backend double-adding the image base. An address at or above the image base is
treated as absolute; a smaller one is already lifted.
backend status
Check whether an optional backend runtime is installed without starting a decompiler server:
decompiler backend status jadx [--json]
The JSON response reports the bridge JAR, Java runtime, official JADX JAR and
detected versions. It exits successfully when the backend is ready and exits
1 with a reasons list when setup is incomplete.
install-skill
Copy the bundled Agent Skill into a supported agent skill directory so Claude Code or Codex learns how to drive the CLI.
decompiler install-skill [names ...] [--agent claude|codex|all] [--dest DIR] [--force] [--json]
With no names, every bundled skill is installed. By default the installer
uses Codex when CODEX_* environment variables are present, otherwise Claude.
Use --agent codex, --agent claude, repeated --agent flags, or
--agent all to choose explicitly. Claude installs under ~/.claude/skills;
Codex installs under $CODEX_HOME/skills when CODEX_HOME is set, otherwise
~/.codex/skills.
Use --dest to copy the skill somewhere else, and --force to overwrite an
existing directory. --json emits a well-formed JSON payload suitable for
piping through jq.
Server selection
When more than one server is running, the inspection/mutation commands need to know which one to talk to. Narrow with any combination of:
--id <SERVER_ID>— exact match.--binary <PATH>— match by binary path (resolved to an absolute path).--backend <angr|ghidra|binja|ida|jadx>— match by backend.
If zero servers match, the CLI errors out and tells you to run
decompiler load. If multiple match, it prints a disambiguation list:
Multiple servers match. Specify --id to disambiguate:
3308b81cf8 backend=angr binary=/…/fauxware
9d77ab8fd4 backend=angr binary=/…/posix_syscall
JSON output
Pass --json on any subcommand to get a structured payload suitable for
downstream parsing. This is the recommended mode for scripts and LLM
callers. Every JSON payload that mentions an address provides both
addr (integer, lifted) and addr_hex (hex string, also lifted), so you
can copy either form without re-formatting:
decompiler list_functions --filter '^main$' --json
# [{"addr": 1821, "size": 184, "name": "main", "addr_hex": "0x71d"}]
decompiler xref_to authenticate --json
# {"addr": 1636, "direction": "to",
# "xrefs": [{"kind": "Function", "addr": 1821, "name": "main", "addr_hex": "0x71d"}, ...],
# "addr_hex": "0x664"}
For decompile/disassemble output, JSON wraps the text in a text field
with escaped newlines. At a terminal this is awkward; pass --raw
instead:
decompiler decompile main --raw # prints the pseudocode directly
decompiler disassemble 0x71d --raw # prints assembly directly
Exit codes
Every CLI command uses these exit codes:
| Code | Meaning |
|---|---|
0 | Success. |
1 | User-visible error — target not found, rename didn't apply, decompile failed, etc. Most failure modes unify to 1 so that shell && chaining works cleanly. |
2 | The requested capability is not implemented for the selected backend (e.g. save on angr). Distinct from 1 so scripts can branch on "unsupported" vs "failed". |
Argparse-level errors (unknown subcommand, missing required argument) also exit
with Python's standard argparse code 2.
Running multiple binaries at once
decompiler load ./my-binary # id=abc1234
decompiler load ./my-binary-2 # id=def5678
decompiler list
# ID BACKEND PID BINARY
# abc1234... angr 4213 .../my-binary
# def5678... angr 4217 .../my-binary-2
#
# (registry: /…/declib/servers)
# Target by ID …
decompiler decompile main --id abc1234
# … or by binary path.
decompiler decompile main --binary ./my-binary-2
# Restart a server cleanly (stop existing, spawn fresh):
decompiler load ./my-binary --replace
# Run an additional server alongside the existing one:
decompiler load ./my-binary --force
# Tear them all down.
decompiler stop --all
You can even mix backends on the same binary — add --force to load to
launch a second server for the same file:
decompiler load ./bin --backend ghidra
decompiler load ./bin --backend angr --force
decompiler decompile main --binary ./bin --backend ghidra
decompiler decompile main --binary ./bin --backend angr
Address formats
DecLib normalizes addresses to a lifted form (relative to the binary's base address), so artifacts stay stable across decompilers. The CLI, though, accepts whatever is natural for the user:
0x71d,1821— lifted0x40071d— absolute (base + lifted)main— symbol name
The CLI converts on the fly. The returned addr fields in JSON output are
always lifted, which matches what the server's artifact dictionaries
use. addr_hex is the same value as a hex string for convenience.
Library-level API
Everything the CLI does is also available as a library — useful when you want to chain operations or integrate DecLib into a larger tool:
from declib.api.decompiler_client import DecompilerClient
# Pick a running server out of the shared registry.
client = DecompilerClient.discover_from_registry(binary_path="./fauxware")
for addr, func in client.functions.items():
if func.name == "main":
print(client.decompile(addr).text)
print(client.disassemble(addr))
for caller in client.get_callers(addr):
print(caller.addr, caller.name)
The three APIs added to power the CLI are also usable directly through
DecompilerInterface (headless/embedded) and DecompilerClient (remote):
list_strings(filter: str | None = None) -> list[tuple[int, str]]get_callers(target: Function | int | str) -> list[Function]disassemble(addr: int) -> str | None
Backends currently implementing them: angr and Ghidra. IDA and Binary Ninja fall back to the default implementations.
Troubleshooting
No running decompiler server matches …
You haven't loaded the binary yet. Run
decompiler load <binary> --backend <backend> first, or use
decompiler list to see what's already running.
Multiple servers match. Specify --id to disambiguate
Two servers match your filters. Either pass --id with one of the printed
IDs, or narrow with --binary/--backend.
Timed out waiting … for server … to start.
The detached server process didn't come up in time (default 5 minutes).
Check backend prerequisites:
- Ghidra:
GHIDRA_INSTALL_DIRmust be set. - IDA/Binary Ninja: their Python bindings must be importable.
- angr: should just work.
No function starts at 0x…
The address is valid in the binary but doesn't correspond to the first
byte of any known function. Use decompiler list_functions to find a
valid start. (Prior to v2 this was reported with the same error as
"decompiler engine failed"; they're now distinct.)
Rename reports success: False (exit 1)
The old name was not found in the function (e.g. it was already renamed,
or you targeted the wrong function).
list_strings looks thin
This is expected on angr (and can happen on Ghidra for stripped binaries) —
list_strings returns only what the decompiler itself identified. Use an
external scanner to see every ASCII constant in the file, then feed the
address back into xref_to / decompile:
strings -a -n 4 ./target
rabin2 -z ./target
readelf -p .rodata ./target
Server-side logs
Spawned servers have their stdout/stderr sent to /dev/null. If you're
debugging server startup, start one by hand in a foreground terminal:
declib --server --headless --decompiler angr --binary-path ./bin --server-id my-srv
That will print log output to the terminal, and the CLI in another terminal
can still drive it via decompiler decompile main --id my-srv.