skylot/jadx · error · RuntimeException

Failed to save JSON file: {}

Error message

Failed to save JSON file: {}

What it means

Thrown by CallGraphExportJson.writeTo(Path) as a plain RuntimeException wrapping an IOException during the call-graph JSON export write. Same pattern as the DOT exporter: mkdirs-for-file then Files.writeString with WRITE/TRUNCATE_EXISTING/CREATE.

Source

Thrown at jadx-commons/jadx-analysis/src/main/java/jadx/analysis/callgraph/CallGraphExportJson.java:46

	private final Gson gson;

	public CallGraphExportJson(ICallGraph callGraph) {
		this.callGraph = callGraph;
		this.gson = new GsonBuilder()
				.disableJdkUnsafe()
				.disableInnerClassSerialization()
				.setStrictness(Strictness.STRICT)
				// .setPrettyPrinting() // TODO: add option for pretty print?
				.create();
	}

	public void writeTo(Path path) {
		try {
			FileUtils.makeDirsForFile(path);
			Files.writeString(path, writeToString(), StandardCharsets.UTF_8,
					WRITE, TRUNCATE_EXISTING, CREATE);
		} catch (IOException e) {
			throw new RuntimeException("Failed to save JSON file: " + path, e);
		}
	}

	public String writeToString() {
		List<Edge> edges = new ArrayList<>();
		Map<Integer, Node> nodeMap = new HashMap<>();
		for (ICallGraphEdge edge : callGraph.edges()) {
			ICallGraphNode from = edge.from();
			ICallGraphNode to = edge.to();
			addNode(from, nodeMap);
			addNode(to, nodeMap);
			Edge jsonEdge = new Edge();
			jsonEdge.from = from.getId();
			jsonEdge.to = to.getId();
			jsonEdge.resolved = edge.isResolved();
			edges.add(jsonEdge);
		}
		List<Node> nodes = new ArrayList<>(nodeMap.values());

View on GitHub (pinned to e738a26571)

Solutions

  1. Ensure the target directory exists and is writable.
  2. Use an absolute, writable path.
  3. Free disk space and retry.
  4. Inspect the wrapped IOException for the OS error.

Example fix

// before
exporter.writeTo(Path.of("/sys/cg.json"));
// after
Files.createDirectories(Path.of("/tmp/cg"));
exporter.writeTo(Path.of("/tmp/cg/out.json"));
Defensive patterns

Strategy: validation

Validate before calling

Path parent = path.getParent();
if (parent == null || !Files.isWritable(parent)) {
    if (parent != null) Files.createDirectories(parent);
}
if (!Files.isWritable(path.getParent())) {
    throw new IllegalStateException("Cannot write to: " + path);
}

Type guard

public static boolean isOutputWritable(Path p) {
    Path parent = p.getParent();
    return parent != null && Files.isWritable(parent);
}

Try / catch

try {
    export.writeTo(path);
} catch (RuntimeException e) {
    if (e.getMessage().startsWith("Failed to save JSON file")) {
        LOG.error("Call-graph JSON export failed for {}", path, e.getCause());
    } else throw e;
}

Prevention

When it happens

Trigger: Calling CallGraphExportJson.writeTo(path) where the file cannot be created or written: permission denied, parent dir not creatable, disk full, invalid path.

Common situations: Output directory not writable; disk full; running in a sandboxed/containerized environment without a writable mount; relative path resolving to a read-only location.

Related errors


AI-assisted analysis of skylot/jadx@e738a26571 (2026-08-14). Data as JSON: /api/errors/d358c81292054290. Report an issue: GitHub.