Library Troubleshooting
Common integration failures for the current C ABI.
ABI Mismatch
Symptom: a binding crashes, reads nonsense fields, or receives
GLN_ERR_INVALID_ARG after updating only the header or only the shared library.
Fix: compile against the installed header that ships with the shared library you load, and verify startup:
if (gln_get_api_version() != GLN_API_VERSION) {
return 1;
}
The current GLN_API_VERSION is 9u.
Treating GLN_OK as Provider Success
Symptom: an operation appears to succeed, but the binding then sees no typed payload or misses a TAN challenge.
Cause: backend operation return values report envelope production. A GLN_OK
function return means *out_result exists. The provider outcome is inside the
envelope.
Fix:
gln_backend_result_t* result = NULL;
gln_status_t s = gln_retrieve_accounts(backend, &result);
if (s != GLN_OK) {
/* No envelope exists. */
}
else if (gln_get_backend_result_outcome(result) == GLN_BACKEND_OUTCOME_SUCCESS) {
/* Use gln_get_backend_result_* accessors. */
}
else if (gln_get_backend_result_outcome(result) == GLN_BACKEND_OUTCOME_ACTION_REQUIRED) {
/* Inspect interrupt and take continuation. */
}
else {
/* Inspect gln_get_backend_result_error(result). */
}
gln_destroy_backend_result(result);
Wrong gln_error_t Size
Symptom: a constructor or helper returns GLN_ERR_INVALID_ARG before doing
useful work.
Fix: initialize every direct error slot with gln_default_error before passing it
to the library:
gln_error_t error = {0};
gln_default_error(&error);
gln_status_t s = gln_open_fints_backend(
&config,
state_store,
continuation_store_or_null,
pin,
&backend,
&error);
Release a populated direct slot with gln_release_error. Do not release the
borrowed error returned by gln_get_backend_result_error; destroying the envelope
releases it.
Envelope Lifetime Bugs
Symptom: host-language strings or rows copied from a result become garbage after cleanup.
Cause: typed views and const char* fields returned from
gln_get_backend_result_*, collection accessors, row accessors,
gln_get_backend_result_error, and gln_get_backend_result_interrupt_info are borrowed from
the gln_backend_result_t envelope.
Fix: copy everything the host needs before calling
gln_destroy_backend_result. Do not destroy typed views separately.
Missing Continuation After an Interrupt
Symptom: an operation returns an action-required envelope, but resume later cannot proceed.
Fix: take the continuation before destroying the envelope:
gln_error_t error = {0};
gln_default_error(&error);
if (gln_get_backend_result_outcome(result) == GLN_BACKEND_OUTCOME_ACTION_REQUIRED) {
gln_continuation_t* continuation = gln_take_backend_result_continuation(result);
if (continuation != NULL) {
gln_status_t save_status = gln_save_continuation(store, resume_id, continuation, &error);
if (save_status != GLN_OK) {
report_continuation_save_failure(&error);
gln_release_error(&error);
/* Keep or retry the in-memory continuation; it was not persisted. */
handle_unsaved_continuation(continuation);
}
else {
gln_destroy_continuation(continuation);
}
}
}
gln_destroy_backend_result(result);
If take returns NULL, the envelope did not carry a continuation or it was
already taken.
Invalid Resume Artifacts
GLN_ERR_RESUME_ARTIFACT_INVALID means a persisted continuation is malformed,
stale, or incompatible with the current parser. Discard the artifact and
restart the original workflow from the application layer. Do not keep retrying
the same bytes.
Resume Input Rejected
Symptom: gln_resume_continuation produces an error envelope with
GLN_ERR_INVALID_ARG.
Fix: match the input kind to the pause:
- TAN: pass a TAN
gln_secret_t*and useGLN_CONTINUATION_INPUT_KIND_NONE, orGLN_CONTINUATION_INPUT_KIND_HHDUC_RESPONSEwhen supplyinghhduc_response_or_null. - Decoupled poll: use
GLN_CONTINUATION_INPUT_KIND_DECOUPLED_POLLand passNULLfor the interactive secret. - Verification of Payee: use
GLN_CONTINUATION_INPUT_KIND_VOP_CONFIRMand passNULLfor the interactive secret.
Destroy the old continuation after the resume attempt. If the resumed operation pauses again, take and persist the new continuation from the returned envelope.
Buffer and Handle Leaks
Every library-owned return value has a matching release function:
| Returned value | Release function |
|---|---|
| Backend result envelope | gln_destroy_backend_result |
char* output | gln_release_string |
| Binary buffer output | gln_release_buffer |
Direct gln_error_t fields | gln_release_error |
| Backend handle | gln_close_backend |
| State store | gln_destroy_state_store |
| Continuation store | gln_destroy_continuation_store |
| Key store | gln_destroy_key_store |
| Revolut token store | gln_destroy_revolut_token_store |
| Wise token store | gln_destroy_wise_token_store |
| Secret | gln_destroy_secret |
| Continuation | gln_destroy_continuation |
Set local pointers to NULL after destroy in complex cleanup paths to avoid
double destroy and use-after-destroy bugs.
Vtable Ownership Bugs
For state and continuation stores, load allocates output bytes and the
library later calls free_payload. save receives library-owned bytes that are
valid only during the callback.
For key stores, output callbacks allocate JSON strings, DER buffers,
signatures, or decrypted keys, and the library later calls free_buffer.
Input buffers and strings are borrowed for the duration of the callback.
A double-free, a retained save buffer, or a buffer freed by the wrong
allocator will corrupt the process heap.
File-Backed Store Collisions
Symptom: two processes pointed at the same state or continuation path block each other or observe stale data.
Fix: scope file-backed stores by provider profile and worker. If parallel workers must operate on one profile, serialize them at the application layer or route persistence through a custom store with explicit concurrency control.
Plugin and Token Failures
Revolut and Wise backend opening can report:
GLN_ERR_PLUGIN_NOT_FOUNDGLN_ERR_PLUGIN_NOT_TRUSTEDGLN_ERR_TOKEN_PERSIST_FAILED
Inspect gln_open_revolut_backend or gln_open_wise_backend direct
gln_error_t output. If gln_get_error_detail returns detail, use the
plugin and token-persist detail accessors to show searched paths, hints, or the
token operation that failed.
Threading Violations
Symptom: stress runs produce sporadic crashes or corrupted state while single-threaded runs pass.
Cause: an individual handle, secret, continuation, or result envelope is
being touched from more than one thread, or gln_init_runtime and
gln_shutdown_runtime are interleaving with operations.
Fix: confine each handle, secret, continuation, and result envelope to a
single thread. Distribute work across distinct handles when parallelism is
needed; distinct handles may be used from distinct threads concurrently.
Call gln_init_runtime once before the operating phase begins and
gln_shutdown_runtime once after it ends.
See Also
- Conventions - status, result envelopes, and ownership.
- Continuations - interrupt and resume model.
- Stores - state and continuation stores.
- Key store - EBICS callback ownership.
- FFI examples - binding patterns.