{"record":{"id":"cf8fb2adead796b5","repo":"github/copilot-sdk","slug":"failed-to-serialize-ffi-json-parameter","errorCode":null,"errorMessage":"Failed to serialize FFI JSON parameter.","messagePattern":"Failed to serialize FFI JSON parameter\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"java/sdk/src/main/java/com/github/copilot/ffi/FfiRuntimeHost.java","lineNumber":345,"sourceCode":"        String copilotHome = options.getCopilotHome();\n        if (copilotHome != null && !copilotHome.isEmpty()) {\n            env.put(\"COPILOT_HOME\", copilotHome);\n        }\n        if (options.getMode() == CopilotClientMode.EMPTY) {\n            env.put(\"COPILOT_DISABLE_KEYTAR\", \"1\");\n        }\n\n        if (env.isEmpty()) {\n            return null;\n        }\n        return jsonBytes(env);\n    }\n\n    private static byte[] jsonBytes(Object value) {\n        try {\n            return MAPPER.writeValueAsString(value).getBytes(StandardCharsets.UTF_8);\n        } catch (JsonProcessingException e) {\n            throw new IllegalStateException(\"Failed to serialize FFI JSON parameter.\", e);\n        }\n    }\n}\n","sourceCodeStart":327,"sourceCodeEnd":349,"githubUrl":"https://github.com/github/copilot-sdk/blob/cd8cf15dc3f9e762615790aaed0a771a0f392755/java/sdk/src/main/java/com/github/copilot/ffi/FfiRuntimeHost.java#L327-L349","documentation":"jsonBytes() serializes the argv/env payloads with Jackson before passing them to the native layer. Jackson's writeValueAsString threw JsonProcessingException (e.g. infinite recursion, unserializable type), which the library converts to IllegalStateException because it indicates a programming error, not a runtime condition.","triggerScenarios":"buildArgvJson or buildEnvJson given an options object graph Jackson cannot serialize: self-referencing structures, custom types without serializers, or a malformed CopilotClientOptions implementation.","commonSituations":"Subclassing/monkey-patching options with unserializable fields (streams, lambdas with cycles); mixing Jackson versions where a custom serializer is incompatible.","solutions":["Inspect the attached JsonProcessingException cause for the offending property.","Ensure CopilotClientOptions contains only simple serializable values (String, int, boolean, Map, List).","Add @JsonIgnore to transient/non-serializable fields.","Pin a compatible Jackson databind version."],"exampleFix":"// before\nclass Options extends CopilotClientOptions { InputStream debugStream; }\n// after\nclass Options extends CopilotClientOptions {\n    @JsonIgnore transient InputStream debugStream;\n}","handlingStrategy":"validation","validationCode":"new ObjectMapper().writeValueAsString(options); // validate serializability before start()","typeGuard":"boolean serializable(Object o) { try { MAPPER.writeValueAsString(o); return true; } catch (JsonProcessingException e) { return false; } }","tryCatchPattern":"try { host.start(e, o); } catch (IllegalStateException ex) { if (ex.getCause() instanceof JsonProcessingException jpe) { log.error(\"unserializable option: \" + jpe.getMessage()); } throw ex; }","preventionTips":["Keep options DTOs to primitives, String, Map, List","Annotate transient fields with @JsonIgnore","Add a unit test serializing CopilotClientOptions"],"tags":["java","jackson","json","serialization"],"backgroundTag":"json-serialization-failed","analyzedSha":"cd8cf15dc3f9e762615790aaed0a771a0f392755","analyzedAt":"2026-09-09T18:32:31.973Z","contentChangedAt":"2026-09-09T18:32:31.973Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}