shadowsocks/shadowsocks-windows · critical · System.Exception

failed to finish preparation

Error message

failed to finish preparation

What it means

Thrown from CipherSetKey when MbedTLS.cipher_reset returns non-zero, fired after a successful cipher_setkey. cipher_reset finalises the context for use; a failure here is rare and indicates the native context is in an inconsistent state after setkey (often a downstream effect of a partially valid cipher/key combo).

Source

Thrown at shadowsocks-csharp/Encryption/AEAD/AEADMbedTLSEncryptor.cs:68

            }

            MbedTLS.cipher_init(ctx);
            if (MbedTLS.cipher_setup(ctx, MbedTLS.cipher_info_from_string(_innerLibName)) != 0)
                throw new System.Exception("Cannot initialize mbed TLS cipher context");

            DeriveSessionKey(isEncrypt ? _encryptSalt : _decryptSalt,
                _Masterkey, _sessionKey);
            CipherSetKey(isEncrypt, _sessionKey);
        }

        private void CipherSetKey(bool isEncrypt, byte[] key)
        {
            IntPtr ctx = isEncrypt ? _encryptCtx : _decryptCtx;
            int ret = MbedTLS.cipher_setkey(ctx, key, keyLen * 8,
                isEncrypt ? MbedTLS.MBEDTLS_ENCRYPT : MbedTLS.MBEDTLS_DECRYPT);
            if (ret != 0) throw new System.Exception("failed to set key");
            ret = MbedTLS.cipher_reset(ctx);
            if (ret != 0) throw new System.Exception("failed to finish preparation");
        }

        public override void cipherEncrypt(byte[] plaintext, uint plen, byte[] ciphertext, ref uint clen)
        {
            // buf: all plaintext
            // outbuf: ciphertext + tag
            int ret;
            byte[] tagbuf = new byte[tagLen];
            uint olen = 0;
            switch (_cipher)
            {
                case CIPHER_AES:
                    ret = MbedTLS.cipher_auth_encrypt(_encryptCtx,
                        /* nonce */
                        _encNonce, (uint) nonceLen,
                        /* AD */
                        IntPtr.Zero, 0,
                        /* plain */

View on GitHub (pinned to 891d971682)

Solutions

  1. Ensure InitCipher is called on a freshly allocated context per session and the context is not shared across threads.
  2. Check that setkey succeeded (error 10) before reset runs — the two are coupled.
  3. Rebuild/replace the native mbedTLS library if resets consistently fail, to rule out library corruption.

Example fix

// before
ret = MbedTLS.cipher_reset(ctx);
if (ret != 0) throw new System.Exception("failed to finish preparation");

// after
if (ret != 0)
    throw new System.Exception($"failed to finish preparation (ret={ret}, cipher={_innerLibName})");
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure context is fresh per session, not reused
if (_encryptCtx != IntPtr.Zero) { MbedTLS.cipher_free(_encryptCtx); _encryptCtx = IntPtr.Zero; }

Try / catch

try { CipherSetKey(isEncrypt, key); /* includes reset */ }
catch (Exception ex) when (ex.Message == "failed to finish preparation")
{ /* recreate context from scratch and retry once */ }

Prevention

When it happens

Trigger: The cipher context entered an error state during setkey that was not caught by its return code; calling InitCipher twice on the same context without proper teardown; native memory corruption from a disposed/reused buffer.

Common situations: Reusing an encryptor context across reconnections without resetting state; a native buffer being garbage-collected or overwritten; threading on a non-thread-safe context.

Related errors


AI-assisted analysis of shadowsocks/shadowsocks-windows@891d971682 (2026-08-13). Data as JSON: /api/errors/77b46cc12bb3b6f7. Report an issue: GitHub.