Skip to content

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 use GLN_CONTINUATION_INPUT_KIND_NONE, or GLN_CONTINUATION_INPUT_KIND_HHDUC_RESPONSE when supplying hhduc_response_or_null.
  • Decoupled poll: use GLN_CONTINUATION_INPUT_KIND_DECOUPLED_POLL and pass NULL for the interactive secret.
  • Verification of Payee: use GLN_CONTINUATION_INPUT_KIND_VOP_CONFIRM and pass NULL for 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 valueRelease function
Backend result envelopegln_destroy_backend_result
char* outputgln_release_string
Binary buffer outputgln_release_buffer
Direct gln_error_t fieldsgln_release_error
Backend handlegln_close_backend
State storegln_destroy_state_store
Continuation storegln_destroy_continuation_store
Key storegln_destroy_key_store
Revolut token storegln_destroy_revolut_token_store
Wise token storegln_destroy_wise_token_store
Secretgln_destroy_secret
Continuationgln_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_FOUND
  • GLN_ERR_PLUGIN_NOT_TRUSTED
  • GLN_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