Release notes

Notable changes are documented here. CHANGELOG.md is the record behind them: this file says what a user has to act on, that one says what changed.

Versions are calendar versions, YYYY.M.D: the number says when a release was cut, and promises nothing about compatibility, so a breaking change is announced in this file — read it before upgrading, rather than a digit.

v2026.11.2

  • The wallet raises its own FetchError, HttpError, RpcError, SignerError, SignerNotFoundError, InconclusiveError and NoDescriptorError, declared in btclib_wallet.exceptions (issue btclib-org/btclib#2709). It raised btclib’s classes of the same names, from btclib.exceptions. Each derives from the same btclib class as before, BTClibRuntimeError or BTClibValueError, so except BTClibException still catches it. Act on it if you catch one of them imported from btclib.exceptions: import it from btclib_wallet.exceptions. TcpPeerTransport still raises bitcoin_core_rpc.FetchError, as it did before; CompactFilterFetcher raises the wallet’s.

  • The Electrum codec is btclib_wallet.electrum (issue btclib-org/btclib#2709). ElectrumFetcher uses it in place of btclib.electrum, so get_tx_merkle returns a btclib_wallet.electrum.MerkleProof. Act on it if you import btclib.electrum, or check that proof’s type against btclib’s class: use btclib_wallet.electrum.

  • CompactFilterFetcher checks the filter headers it is served against more peers (ISS 352). It takes them as cross_check, none by default, and compares their cfcheckpt and cfheaders with those transport serves, as BIP157’s Client Operation recommends. At the first disagreement it downloads the block, and drops a peer whose filter does not match its own header or leaves out one of the block’s output scripts. Where two filters differ only in the scripts the block’s inputs spend, the call fails with FetchError, naming that peer’s index in cross_check, and drops no peer. transport is a read-only property, the peer that serves the chain: when that peer is dropped, the first peer of cross_check takes its place. Act on it if you assign transport: build a new fetcher. TcpPeerTransport.close raises a failed socket close as bitcoin_core_rpc.FetchError, as its send and receive raise theirs, where it raised the OSError. Act on it if you catch that OSError: catch bitcoin_core_rpc.FetchError.

  • HistoryReader lists the transactions that pay to or spend from some scripts (ISS 629). It is a protocol of btclib_wallet.fetch, beside Broadcaster, FeeEstimator and BalanceReader. BitcoinCoreFetcher implements it as get_history, without the node’s wallet; the other fetchers do not. CompactFilterFetcher.scan_blocks refuses an empty script with BTClibValueError, an empty script to look for, where it looked for it. Act on it if you pass an empty script: leave it out.

  • scan_outputs and scan_transaction_outputs refuse a malformed label (ISS 474). A label that is not 33 bytes, a label tweak that is not 32 bytes in [1, n-1], and labels that are not a mapping are refused with BTClibValueError or BTClibTypeError, whether or not the label matches. scan_outputs, and scan_transaction_outputs without the secp256k1 extra, ignored an unmatched one and raised AttributeError for labels that are not a mapping. With the extra, scan_transaction_outputs refused them already, raising a plain TypeError where it raises BTClibTypeError, a TypeError too. scan_transaction_outputs runs the Python scan with at most three labels or at least ten outputs per label, and otherwise calls the bindings where they serve; scan_outputs always runs the Python scan. Act on it if you pass labels of your own making: pass label_lookup’s map.

  • A tr() branch with any count of subtrees but two reports a fault in the subtrees read before the count (ISS 608). parse reads the first subtree, and the second where a comma follows the first, before it refuses the count, as Core does. A bad key or leaf among them raises its own refusal, such as invalid key expression or tr() leaf is not a valid descriptor function, where it raised a tr() branch takes two subtrees, <n> given. Act on it if you match that refusal: match the new message.

btclib’s main no longer declares the seven exceptions or btclib.electrum, and its next release waits for this one, the first that does not import them from btclib (issue btclib-org/btclib#2709).

CHANGELOG.md’s own v2026.11.2 section has the rest.

v2026.10.31

  • A descriptor no longer writes itself into a psbt: update_psbt_input and update_psbt_output are functions of btclib_wallet.psbt (ISS 620). descriptor.update_psbt_input(psbt, vin_i, index, prv_keys) becomes update_psbt_input(descriptor, psbt, vin_i, index, prv_keys), and the same for update_psbt_output, on every descriptor class: Descriptor, PkDescriptor, PkhDescriptor, WpkhDescriptor, ShDescriptor, WshDescriptor, MiniscriptDescriptor, MultiDescriptor, ComboDescriptor, AddrDescriptor, RawDescriptor, TrDescriptor and RawTrDescriptor. The new Descriptor.expansion(index, prv_keys) returns the Expansion they write. The checks run in this order: the types of the descriptor, the psbt and the map index, each a BTClibTypeError, a map index that is not an int (a bool, or an integer of another type such as numpy’s int64) included; prv_keys and the derivation index; the descriptor’s refusal (combo(), addr() and raw(), or a hardened step without its private key); then the map-index range and the paid script. At v2026.10.29 prv_keys and the derivation index came first, then the map-index range, then a hardened step without its private key, the paid script, and combo(), addr() and raw(). Nothing checked the map index’s type: a bool or a numpy integer in range was taken as that map, and another type raised a TypeError, or a BTClibValueError out of range, at or just after the range check. A psbt that is not a Psbt raised an AttributeError at the same check. DescriptorWallet.update_psbt_input and update_psbt_output call these functions and change the same way, after their own position check. Act on it if you call a descriptor’s update_psbt_input or update_psbt_output: import the function from btclib_wallet.psbt and pass the descriptor first. Act on it if you pass a map index that is not an int: pass an int. Act on it if you catch the BTClibValueError of a float map index, or the AttributeError of a psbt that is not a Psbt: catch BTClibTypeError.

  • miniscript_sizer, satisfaction_sizer and miniscript_solver move from btclib_wallet.descriptors to btclib_wallet.psbt (ISS 620). Act on it if you import them: import them from btclib_wallet.psbt.

  • account_descriptors moves from btclib_wallet.descriptors to btclib_wallet.bip44 (ISS 620). Act on it if you import it: import it from btclib_wallet.bip44.

  • A { in or after a key, timelock or hash fragment changes the refusal (ISS 607). In wsh() and in a tr() leaf, parse cuts such a fragment as Core’s Expr does, counting { as a bracket. The BTClibValueError it raises is the key’s own refusal for a key fragment, such as not a key expression or invalid key expression, where it was unbalanced brackets in <fragment>() or invalid key expression. For older() and after() it is A function is needed within P2WSH in wsh() and tr() leaf is not a valid descriptor function in a tr() leaf, and for a hash fragment invalid <fragment>() hash: hex expected, where each was unbalanced brackets in <fragment>(). Act on it if you match any of these refusals: match the new message.

  • KeyGroup.keys holds the same keys (ISS 620). KeyGroup builds them with key_data_from_bip32_key, the public name of the helper it called before, whose code is unchanged. Nothing to act on.

These are the wallet’s side of bip32 and descriptors leaving for a package of their own (btclib-org/.github#1811): the next releases import them from bip32-descriptors.

CHANGELOG.md’s own v2026.10.31 section has the rest.

v2026.10.29

  • BIP322 finds a proof inconclusive where a taproot input carries an annex (ISS 516). assert_as_valid raises InconclusiveError, taproot annex is reserved for future extensions, and verify returns False, where both accepted the proof as valid. BIP341 reserves the annex for future extensions. Act on it if you verify proofs whose taproot witness carries an annex.

  • A miniscript hash refuses whitespace and anything else that is not hex (ISS 507). sha256(), hash256(), ripemd160() and hash160() refuse whitespace and anything else that is not an even, non-zero count of hex characters, as Core’s IsHex, in a descriptor as in a miniscript, as BTClibValueError naming the fragment: invalid sha256() hash: hex expected. A hash with whitespace inside or around it was read without the whitespace, which changed the descriptor’s checksum. Act on it if you pass such a hash: remove the whitespace. Act on it if you match the refusal of a hash that is not hex: match the new message.

  • CompactFilterFetcher checks each block it downloads with btclib’s Block.assert_valid_contextual at the block’s height (issue btclib-org/btclib#2705). scan_blocks fails with FetchError on these blocks, which it accepted. Below the segwit height, a block with witnesses its coinbase commits to fails as block: unexpected witness. A block with a time lock not yet reached, compared with the parent’s median time past where BIP113 binds, fails as block: non-final transaction: <txid>. Where the block’s height carries the rule, a block whose version is too low fails as block: invalid version (outdated): …, and one whose coinbase names another height as block: invalid coinbase height: …. SECURITY.md says which checks a downloaded block passes.

  • CompactFilterFetcher synchronizes a low-work chain with btclib’s HeadersSyncState (issue btclib-org/btclib#2706). A batch of headers the sync refuses fails the call with FetchError, which does not say which check failed: in the first download as headers <first> to <last> are not a chain the rules allow, and in the second as headers <first> to <last> are not the chain first served. Act on it if you match the messages of those refusals.

  • SECURITY.md verifies a release by this repository’s id (ISS 517). Its command passes --owner btclib-org to gh attestation verify and compares the certificate’s sourceRepositoryIdentifier with the id, where it passed --repo btclib-org/btclib-wallet, which SECURITY.md says refuses every release signed before a rename. Act on it if you verify releases with --repo: run SECURITY.md’s command.

CHANGELOG.md’s own v2026.10.29 section has the rest.

v2026.10.26

  • BIP352 skips a taproot input whose control block has an invalid length (ISS 556). pub_key_from_input returns None for a script-path spend whose control block is not 33 bytes plus a multiple of 32, up to 128 of them, where it returned the input’s output key. BIP341 allows no other length, and bitcoin/bips#2339 has BIP352’s reference implementation skip it.

  • The release’s attestation names its signer workflow at a commit (issue btclib-org/.github#1870). release.yml calls reusable-build.yml of btclib-org/.github at a tagged commit, where it called it at refs/heads/main. Act on it if you verify a release with --signer-workflow naming reusable-build.yml@refs/heads/main: name the workflow without a ref, as SECURITY.md does.

CHANGELOG.md’s own v2026.10.26 section has the rest.

v2026.10.25

  • btclib-wallet requires ecc, the renamed btclib-ecc (ISS 466, ISS 477). It requires btclib>=2026.10.24 and ecc>=2026.10.23, and no longer installs btclib-ecc. The exceptions of the curve package are EccException, EccTypeError, EccValueError and EccRuntimeError of ecc.exceptions, where they were the BTClibEcc* ones of btclib_ecc.exceptions, with no alias. Act on it if you catch one that this package lets through, as BIP322 verification does for a legacy signature whose recovered key is the point at infinity: catch the new name. Act on it if you import btclib_ecc and relied on this package to install it: install and import ecc. Act on it if you set BTCLIB_ECC_NO_LIBSECP256K1 to turn the libsecp256k1 bindings off: only ECC_NO_LIBSECP256K1 does.

  • btclib-wallet requires bip39-slip39 and raises its exceptions (issue btclib-org/bip39-slip39#106). It no longer installs btclib-mnemonics, which bip39-slip39 replaces. The mnemonic functions raise Bip39Slip39ValueError and Bip39Slip39TypeError, which share the base class Bip39Slip39Exception, where they raised BTClibMnemonicsValueError and BTClibMnemonicsTypeError, under BTClibMnemonicsException. Act on it if you catch any of them: catch the new names. Act on it if you import btclib_mnemonics and relied on this package to install it: install and import bip39_slip39.

  • extract_tx refuses a fee above max_fee_rate (ISS 459). The default is DEFAULT_MAX_FEE_RATE, Core’s -maxfeerate of 0.10 BTC per kvB, priced on the final virtual size. A missing utxo now raises even with verify_scripts=False, because the check needs every input’s amount. Act on it if you extract a psbt with a higher fee: pass a higher max_fee_rate, or None to skip the check.

  • PsbtOut.script_pub_key is None where the output has no script (PR 483). It is b"" where PSBT_OUT_SCRIPT is present and empty, which a v2 psbt now keeps through serialization. Its JSON form is null for an absent script. The constructor’s default is None, where it was b"". Act on it if you read the field or its JSON, or clear a v2 script: handle None, and assign None to clear one.

  • BIP322 assert_as_valid refuses more (ISS 502, ISS 493). It refuses a proof whose sigop cost, with to_spend’s, is over MAX_BLOCK_SIGOPS_COST; OP_CODESEPARATOR in a P2WSH script or a tapscript leaf; a psbt whose signed message is not the one verified; and an additional legacy input with no Non-Witness UTXO. Act on it if you verify such proofs: they were accepted, and Core or BIP322 refuses them.

  • CompactFilterFetcher downloads a chain that lacks the minimum work twice (ISS 354), as Core’s headers presync does, so memory is bounded before its work is known. A first synchronization from the genesis block downloads the headers twice. SECURITY.md says what it checks.

  • strip_checksum quotes no checksum (ISS 478). A wrong checksum is “invalid descriptor checksum”. Act on it if you match : <checksum> expected: match the new message, and call add_checksum for the right checksum.

  • Some descriptor refusals change their message (ISS 437, 438, 475, 476). parse reads the keys of a multisig before the structure around it, and names Core’s fault for a refused older() or after() number, for a multi() in tapscript and for a multi_a() in P2WSH. The same inputs are refused; only the messages change. Act on it if you match the text of a refusal: match the new one.

  • A taproot input without PSBT_IN_SIGHASH_TYPE accepts SIGHASH_ALL (ISS 461), and finalize builds the witness of a multi_a leaf (ISS 462).

  • New: EsploraFetcher.get_balance, with Balance and BalanceReader (ISS 458), and ElectrumFetcher.get_block_headers (ISS 330).

CHANGELOG.md’s own v2026.10.25 section has the rest.

v2026.10.18

  • parse and multipath_descriptors refuse a key with a space before or after it (ISS 443), as Bitcoin Core does: “Key is invalid due to whitespace”. Act on it if you pass a descriptor with a space by a key: remove the space.

  • multipath_descriptors reads a descriptor as parse does (ISS 393, ISS 370, ISS 448, ISS 450, ISS 451). It hands every expansion to parse, so it refuses a descriptor whose expansion parse refuses, where it returned the expansion, and it parses a descriptor with no multipath step, where it returned it unread. It takes an optional network; with none, the network of a key is not checked. Where a descriptor has several faults, parse and multipath_descriptors report the one Bitcoin Core reads first, a musig() participant’s fingerprint and origin faults carry the musig(): prefix in both, and parse gives Core’s message for a musig() with a step in both its participants and its path. Act on it if you match the text of a refusal: match the new one. Act on it if you expanded a descriptor parse refuses: write it as Core reads it.

  • CompactFilterFetcher finds the blocks that touch a wallet’s scripts over p2p (ISS 329): it reads the BIP157/158 filters one peer serves and downloads only the blocks whose filter matches.

CHANGELOG.md’s own v2026.10.18 section has the rest.

v2026.10.16

  • btclib-wallet 2026.9.30 to 2026.10.9 fail at import beside btclib-ecc 2026.10.16 (issue btclib-org/.github#1742). import btclib_wallet.bip32 raises ImportError, and so does importing psbt, descriptors or wallet: btclib-ecc 2026.10.16 renames point_from_octets to point_from_octets_var, with no alias. Upgrade to this release, or keep btclib-ecc below 2026.10.16.

  • silent_payments.output_keys returns the keys in the order of the addresses (ISS 415). Through 2026.10.9 it grouped them by scan key, so for [A1, B, A2], A1 and A2 sharing a scan key, key 1 was A2’s and key 2 was B’s. A transaction that paired key i with address i paid B’s amount to A2 and A2’s amount to B. Upgrade. Act on it if you reordered the keys yourself: stop reordering them.

Breaking changes

  • btclib-wallet requires btclib 2026.10.16 and btclib-ecc 2026.10.16 (issue btclib-org/.github#1742). Act on it if you pin either lower. Their own release notes list what else they change.

  • block_header_from_raw takes a required, keyword-only network (ISS 328). It checks the proof of work against that network’s limit, where it used mainnet’s for every network. So the Electrum, Esplora, Bitcoin Core and Core REST fetchers read regtest and signet headers. Act on it if you call it: pass the network’s name.

  • ElectrumFetcher.assert_network raises BTClibValueError for a server of another chain (ISS 341). A mainnet fetcher over a regtest server raised FetchError, which FallbackFetcher falls through. Act on it if you catch FetchError there: catch BTClibValueError.

  • psbt.combine keeps the first psbt’s value for a key that several psbts hold (ISS 325), as Bitcoin Core does. It kept the last one’s. Act on it if a later psbt was meant to win: put it first.

  • A psbt is refused for a BIP32 derivation key Bitcoin Core refuses (ISS 342, ISS 356, ISS 377). In the global map the key is a 78-byte xpub whose public key is a point. In an input or output map it is a 33- or 65-byte public key that is a point. Parsing, validating and serializing refuse any other. Act on it if you build such a psbt: put the key BIP174 names there.

  • psbt_utils.deserialize_map takes a required which (ISS 351): the map’s name, global, input or output, for its refusal. Act on it if you call it: pass the name.

  • multipath_descriptors refuses what Bitcoin Core refuses (ISS 299, ISS 301, ISS 353, ISS 365, ISS 367, ISS 397). It expanded these, and refuses them: a key origin fingerprint that is not 8 hex digits; a step with a repeated value, as <1;1>; a path element that is no index; a key with a [ and no ], or more than one ]; a musig() outside tr() and rawtr(); a musig() with a path and a ranged participant; a hardened item of a step in a musig() path; a multipath step in the argument of a function that takes no key, as older(<1;2>). parse refused each expansion, except those of the repeated value and of older(<1;2>), and the unhardened ones of a hardened musig() step. Act on it if you hold such a descriptor: write it as Core reads it.

  • DescriptorWallet refuses two branches that share a script (ISS 411, ISS 414), however their descriptors are spelled: a key origin added, a child xpub for a derivation step, or a fixed chain that is an index of a ranged one. position_of answered the first branch for both. The constructor derives every chain at index 0, so a chain it cannot derive, such as a hardened step with no prv_keys, is refused there. Act on it if you built such a wallet: give each branch its own chain, and the private keys a hardened step needs.

  • Descriptor.index_of and RangedWallet.position_of refuse a last_index that is not a non-negative integer (ISS 407, ISS 413): a negative one with BTClibValueError, where they answered None, and a str, None, bytes, float or bool with BTClibTypeError. KeyExpression.sec refuses an index that is not an int from 0 to 2**31 - 1, as Descriptor does, a fixed key’s included, and so do Miniscript.script and Miniscript.satisfy through it (ISS 383, ISS 404). Act on it if you pass another: pass an int in range.

  • hwi looks a bare executable name up in the absolute directories of the PATH alone (ISS 416, ISS 422). An empty or relative PATH entry is skipped, and on Windows the current directory is not searched and a bare name with no period is looked for as <name>.exe only. A name with a directory part is checked as the file it names. Act on it if hwi was found another way: put its directory on the PATH as an absolute path, or pass the absolute path.

  • The messages of some refusals change, and print no key bytes (ISS 351, ISS 388, ISS 299, ISS 367, ISS 370, ISS 358, ISS 374): a duplicated psbt map key, the MuSig2 and silent-payment key checks, a partial signature, bip85.mnemonic_from_root_key’s words and lang, an extended key whose public key is not a point, a multipath step glued to a key, and the Esplora and Core fee estimate replies. parse refuses a multipath step in a musig() path as multipath key expression: use multipath_descriptors first. parse and multipath_descriptors report the fault Bitcoin Core reads first in a descriptor with more than one. A refused partial signature or MuSig2 key chains no __cause__, and bip85.mnemonic_from_root_key refuses a words that is not an int with BTClibTypeError, where it raised BTClibValueError. Act on it if you match the old text: match the new one. Act on it if you catch BTClibValueError for words: catch BTClibTypeError too.

New

  • btclib_wallet.dlc builds, signs and verifies Discreet Log Contracts (ISS 331, ISS 332): the funding, execution and refund transactions of dlcspecs, their adaptor signatures, and the oracle announcements and attestations.

CHANGELOG.md’s own v2026.10.16 section has the rest.

v2026.10.9

  • A refused import, a password in an Esplora URL, a key that is not a point, a mistyped extended key and a BIP38 password are no longer quoted (GHSA-8qrf-j8jh-cx23). Releases 2026.9.24 to 2026.10.8 can put a private key, a hash of one or a password in an exception, the Esplora URL from 2026.9.30 (PR 311). Upgrade, and treat a key or password that such an error may have logged or shown as exposed.

  • The messages of those refusals changed. core_import.assert_imported raises import refused for request {position}: error code {code}. EsploraFetcher refuses any @ in base_url, and an invalid port as invalid base_url port. A key that is not a point is refused with its prefix byte only, and a bad checksum or a non-ASCII character in an extended key with fixed text. bip38 refuses a password that is not UTF-8 with BTClibValueError instead of UnicodeEncodeError. An out-of-range derivation index and a master fingerprint of the wrong length are refused without their value. A caller that matches on the old messages, or catches UnicodeEncodeError from bip38, has to change.

  • The btclib floor is 2026.10.8 (ISS 306). Through 2026.10.7, btclib quotes a refused network name whole, so KeyWallet(network=wif) put the WIF in the error (GHSA-c6h8-5hv5-3gv7).

  • Descriptor.expand(index, prv_keys) is new. It returns the scripts and the provider at index, what script_pub_keys and provider return, from one derivation of each key, as Bitcoin Core’s Descriptor::Expand does. A caller that needs both can call it instead of the two, and derive every key once rather than twice (ISS 303). provider called alone also derives each key once now. Nothing is removed or changed in what the existing methods return.

CHANGELOG.md’s own v2026.10.9 section has the rest.

v2026.10.8

  • A refused multipath step in a key origin is no longer quoted. In v2026.10.7, a step such as [deadbeef/<WIF;1>] was put in the error message of descriptors.parse and descriptors.multipath_descriptors, so a private key (WIF) in it was echoed. This release leaves the step out (PR 285). Upgrade if you parse descriptors that may hold a private key and log or show the errors. Treat a key in such an error from 2026.10.7 as exposed (GHSA-62jf-v327-86h5, published after this release).

  • multipath_descriptors and parse refuse a multipath step that is not a whole path element. <0;1>h, <0;1>', 0<0;1>, <0;1>> and <<0;1>, in a key path or a key origin, raise BTClibValueError “Key path value is not a valid uint32” (ISS 280). Bitcoin Core refuses each, with that message except <0;1>> and <<0;1> in a key origin, which it refuses as a multipath step. v2026.10.7’s multipath_descriptors expanded them, except <0;1>> and <<0;1> in a key origin, which it refused as Core does. Write a hardened step as <0h;1h>. A caller that matches on the message of another step in a key origin finds the step gone from it: the message is “Key path value specifies multipath in a section where multipath is not allowed”.

CHANGELOG.md’s own v2026.10.8 section has the rest.

v2026.10.7

  • btclib-wallet requires btclib 2026.10.5, which fixes GHSA-9r97-9x22-2pp4 and GHSA-rw95-w37r-537w: verifying a taproot input took time linear in the transaction’s inputs, and each legacy or segwit v0 signature check computed its hash from scratch. bip322.verify runs that verification on a signature it was given. Upgrading btclib-wallet installs it.

  • request_signatures refuses to send a psbt asking for a sig_hash type other than SIGHASH_ALL or SIGHASH_DEFAULT. It raises on an input asking for SIGHASH_NONE, SINGLE or ANYONECANPAY, before the signer is called; pass the types you accept as allowed_sig_hash_types. SoftwareSigner applies its own allow-list, so to sign such a psbt with it, call its sign_psbt directly with allowed_sig_hash_types.

  • musig2.partial_sign and frost.partial_sign refuse an input without its non_witness_utxo, unless every input is taproot and none asks for ANYONECANPAY (GHSA-v4gq-j2v2-c4jp). Give each input the transaction it spends as its non_witness_utxo. Pass require_non_witness_utxo=False only where you know every witness_utxo amount is right.

  • A psbt updated from a descriptor carries a derivation for every key, as Bitcoin Core’s does. A key the descriptor gives no origin has an entry under its own fingerprint, so hd_key_paths and taproot_hd_key_paths are not empty for such a descriptor.

  • HwiSigner.sign_psbt refuses a psbt asking for a sig_hash type other than SIGHASH_ALL or SIGHASH_DEFAULT. It raises before hwi runs; pass the types you accept as its keyword-only allowed_sig_hash_types. request_signatures does not pass its allow-list on, so to send such a psbt to a device, call sign_psbt directly and hold its answer with assert_signatures_only(psbt, answer) before combine.

  • A ScriptWallet psbt carries a derivation for every key, as a descriptor’s does. A key of a KeyGroup given no origin has an entry under its own fingerprint, so hd_key_paths is not empty for such a group. Of two explicit origins for one key, the first is written, not the last.

  • The attestation bundle is attached as v<version>.intoto.jsonl from v2026.10.4 on, where earlier releases attach v<version>.attestation.jsonl. A script that downloads it by name, or passes it to gh attestation verify --bundle, uses the new name.

  • sign, request_signatures and HwiSigner.sign_psbt raise on an input asking for SIGHASH_SINGLE with no output at its index, whatever allowed_sig_hash_types says. Such a signature commits to no output, so whoever holds it can spend the input to any output. Give the input an output at its index, or ask for another type. finalize still checks a signature of this kind.

  • A regtest silent payment address starts with sprt1, as Bitcoin Core’s regtest requires, where earlier releases give tsp1. keys_from_address reads both, with the network type "test". Make a stored regtest tsp1 address again with address_from_keys(..., "regtest"), and update any tsp1 string a test compares against.

  • Bolt11Invoice.from_invoice refuses malformed or repeated fields. A p, s, h or n of the wrong length, a second p, s, d, h or n, an x, c or 9 starting with a zero word, and an amount with a leading zero raise BTClibValueError, where earlier releases skipped a wrong-length field, read the first of a repeated one, or read an amount without its leading zeros. This includes an invoice that an earlier sign wrote with expiry=0 or min_final_cltv_expiry=0: sign it again.

  • A psbt keeps an explicit sig_hash_type of 0 through serialize and combine. sign refuses an ECDSA input asking for it after a round trip through bytes too, where it signed SIGHASH_ALL. HwiSigner.sign_psbt and request_signatures refuse SIGHASH_DEFAULT on an input not known to be taproot, where a device signed SIGHASH_ALL. To sign such a psbt, ask its creator for SIGHASH_ALL or remove the field.

  • silent_payments.output_keys refuses an address of another network than its network argument, "mainnet" by default, as Bitcoin Core’s decoder does. An sprt, tsp or sp address that is not that network’s raises BTClibValueError, so a caller paying testnet, signet or regtest passes the network too.

  • wallet_policy_descriptor, wallet_policy_address and wallet_policy refuse a template BIP388 does not allow. A top-level multi() or sortedmulti(), and a pk() inside sh(), raise BTClibValueError; so do a top-level pk(), combo() or rawtr() in the first two, where earlier releases gave a descriptor or an address. Read such a descriptor with descriptors.parse.

CHANGELOG.md’s own v2026.10.7 section has the rest.

v2026.10.4

  • Signing refuses a sig_hash type other than SIGHASH_ALL or SIGHASH_DEFAULT unless the caller allows it (GHSA-qq38-77mp-j6wr). sign, SoftwareSigner.sign_psbt, musig2.partial_sign and frost.partial_sign raise on an input asking for SIGHASH_NONE, SINGLE or ANYONECANPAY; pass the types you accept as allowed_sig_hash_types. request_signatures passes none, so call SoftwareSigner.sign_psbt directly to sign such a psbt. A legacy SIGHASH_SINGLE input with no output at its index is refused whatever is allowed, and ecdsa_sig_hash and PsbtView.ecdsa_sig_hash refuse it too. finalize, assert_signed and assert_signatures_only refuse an ECDSA signature other than SIGHASH_ALL on an input without PSBT_IN_SIGHASH_TYPE; set the field on a psbt whose signatures use another type.

  • sign, SoftwareSigner.sign_psbt and Psbt.assert_signable refuse an input without its non_witness_utxo, unless every input is taproot and none asks for ANYONECANPAY (GHSA-v4gq-j2v2-c4jp). Give each input the transaction it spends as its non_witness_utxo. Pass require_non_witness_utxo=False only where you know every witness_utxo amount is right: a wrong one can be spent as fee.

  • btclib-wallet requires btclib 2026.10.4, btclib-ecc 2026.10.2 and btclib-secp256k1 0.8.0.10. btclib 2026.10.4 fixes GHSA-9fr5-46w5-5f9r, which made bip322.verify run for minutes on a crafted signature; 0.8.0.10 fixes GHSA-8h6f-34jj-7p6c, an invalid-curve oracle in silentpayments.scan_outputs. Upgrading btclib-wallet installs btclib and btclib-ecc; the secp256k1 extra installs the bindings.

  • Verifying a release’s attestation names a new signer and the tag. gh attestation verify takes --signer-workflow btclib-org/.github/.github/workflows/reusable-build.yml@refs/heads/main and --source-ref refs/tags/v<version>; SECURITY.md has the command. Earlier releases keep reusable-attest.yml.

  • descriptors.parse refuses a key of another network. A WIF or an extended key whose prefix is not the network’s raises BTClibValueError there, where the WIF was accepted and the extended key was refused only by script_pub_keys(). A caller that parses a descriptor for a network its keys are not of has to pass the right network.

  • bip39.mxprv_from_mnemonic and electrum.mxprv_from_mnemonic refuse a falsy passphrase of another type. An empty bytes, 0, False or [] derived the wallet without a passphrase; pass None or "" for that.

  • A miniscript repeating a public key is refused under different spellings of it. descriptors.parse does so as Bitcoin Core does, except for a key with a hardened step.

  • A malleable miniscript keeps neither “s”, “f” nor “e”. Miniscript.properties drops them, and is_signature_required is false for it.

  • join empties the signatures of a version 0 psbt, and sign updates a version 2 psbt’s modifiable flags. Sign the joined psbt again. join refuses a version 2 psbt signed with anything but SIGHASH_NONE|ANYONECANPAY.

  • extract_tx verifies by default. It runs every input’s scripts and checks that the outputs do not exceed the inputs, so it raises on a psbt it extracted before: one with a bad signature, a missing utxo or outputs above the inputs. Pass verify_scripts=False to extract without the check.

  • join refuses to change the lock time or the tx version of a signed version 2 psbt. The signatures would stop verifying. Join the psbts before they are signed, or give them the same lock time and tx version.

  • Miniscript.has_duplicate_keys, is_sane, is_sane_subexpression and insane_sub are methods. Call them with parentheses; without them, if node.is_sane: is always true, and mypy reports it as truthy-function. Pass prv_keys, the mapping parse fills, to derive a hardened step: descriptors.parse refuses wsh(or_i(pk(XPRV/1h),pk(<the key it derives>))). Without its private key, a key with a hardened step is still compared as written.

  • descriptors.parse refuses a threshold no script can meet. A multi() or sortedmulti() threshold of zero or above its key count, and a multi_a() or sortedmulti_a() one of zero, above its key count, or of more than 999 keys, raise BTClibValueError, where they were stored. A thresh() of more than 1000 arguments is refused too, a limit of this library.

  • A number of over 4300 digits is a BTClibValueError. A derivation index, a threshold, an older() or after() number and a @N of that size raised the built-in ValueError; they are refused at ten digits now. Bolt11Invoice.from_invoice refuses an amount of more than 20 digits, which its amount parser read up to 4300 digits.

  • A prv_keys that is not a mapping raises BTClibTypeError. The descriptor, KeyExpression, Miniscript and DescriptorWallet methods taking it, and descriptors.normalized, check it as parse does, where a string raised AttributeError.

  • SilentPaymentOutput’s repr leaves out prv_key_tweak. The tweak is a secret; read the field, not the repr, where you need it.

CHANGELOG.md’s own v2026.10.4 section has the rest.

v2026.9.30

A binary-string entropy passed as bytes is refused with a BTClibTypeError by mnemonic.entropy’s functions taking one, which read it as the digits it spells; pass the str instead. A mnemonic that is not a str is refused with a BTClibTypeError rather than a bare TypeError or AttributeError.

mnemonic.slip39.master_secret_from_mnemonics and mxprv_from_mnemonics refuse shares passed as an iterable that is not a sequence – a set, a generator, an iterator, dict.keys() – with a BTClibTypeError, where they read it; pass a list or a tuple instead.

mnemonic.slip39.mnemonics_from_master_secret refuses with a BTClibTypeError a groups passed as a set or a dict, and a bool as a threshold, a member count or the iteration exponent, where it read them; pass a list or a tuple of integer pairs, and integers. mnemonic.entropy.bin_str_entropy_from_rolls refuses the same way rolls passed as a set or a generator and a bool as a roll or as bits.

mnemonic.slip39.Share refuses with a BTClibTypeError a value that is not bytes – a bytearray or a list of ints included – and a bool as an integer field or a non-bool as extendable, where it held them; pass bytes, integers and a bool. mxprv_from_mnemonics refuses a falsy passphrase of another type, 0, [] or b"", where it read the empty passphrase; pass None or "". mnemonics_from_master_secret refuses an entropy_source answering anything but bytes or a bytearray of the length asked – a 1-of-1 backup read a list, a memoryview or a draw of another length; return exactly that.

fetch.BitcoinCoreFetcher and fetch.BitcoinCoreRestFetcher refuse with a BTClibTypeError a client that is not a BitcoinCoreRpcClient or a BitcoinCoreRestClient respectively, where a stand-in answering the methods they call worked; pass an instance of that class or a subclass. fetch.ElectrumFetcher refuses a timeout that is no number, a bool, not positive, or above threading.TIMEOUT_MAX, which it handed as given to a transport of the caller’s; pass a positive number of seconds. fetch.EsploraFetcher refuses a base_url carrying a user or a password, or without a host, which a transport of the caller’s received; pass the url without them, and let that transport add the credentials.

tx_builder.build_psbt, coin_selection.select_coins, knapsack and single_random_draw require change_script_pub_key: a call that left it out, and so sent every leftover satoshi to the fee, raises a TypeError; pass the change script, or None for a sweep.

tx_builder.build_psbt refuses with a BTClibValueError a fee above max_fee, 0.10 BTC unless raised, and an output being paid below its dust threshold. It refuses a non-zero lock_time too when every input’s sequence is final: set a non-final PsbtIn.sequence, such as 0xfffffffe, on an input for the lock time to bind.

psbt.sign refuses with a BTClibValueError a psbt paying a silent payment it used to sign: one whose silent payment output has no script yet – run psbt.silent_payments.set_input_share or set_global_share, then set_output_scripts, before signing – one with an input’s sighash other than SIGHASH_ALL, and one whose shares, scripts or modifiable flags psbt.silent_payments.assert_as_valid refuses. psbt.extract_tx refuses what that function refuses, and a silent payment output with no script; check_validity=False extracts as before. psbt.combine refuses two psbts carrying different scripts for one silent payment output.

tx_builder.build_psbt also refuses with a BTClibValueError a fee above what max_fee_rate, 0.10 BTC/kvB unless raised, asks of the estimated virtual size, however far below max_fee it is; pass a higher max_fee_rate where such a fee is meant.

A psbt with an input whose witness_utxo is not, in amount and script, the output of its non_witness_utxo the outpoint names is refused with a BTClibValueError by Psbt.assert_valid, and so by Psbt.parse and every function validating the psbt, where it was accepted and the witness_utxo read; set it to that output, or drop it. bip322.assert_as_valid refuses a proof of funds in which it contradicts the transaction an earlier input carries.

psbt.estimated_input_sizes refuses with a BTClibValueError a psbt_in whose non_witness_utxo is not the transaction the tx_in’s outpoint names, and one whose witness_utxo differs from the output that outpoint names, where it answered an estimate; pass the TxIn the input is spent by, and a witness_utxo equal to that output.

psbt_signer.request_signatures refuses a psbt paying a silent payment that psbt.sign would refuse, before the signer is asked, where it sent it on. psbt.silent_payments counts an input by the script it spends, and refuses with a BTClibValueError a counted input whose key the psbt does not carry where it left the input out: give every p2wpkh, p2pkh and p2sh-p2wpkh input a PSBT_IN_BIP32_DERIVATION whose key the script commits to, and a p2sh input its redeem script. A taproot input whose internal key is BIP341’s NUMS point is counted unless PSBT_IN_TAP_MERKLE_ROOT or a control block proves it. set_output_scripts refuses until every counted input has a share, where it derived the scripts from the shares there were.

hwi.HwiSigner.sign_psbt called directly refuses such a psbt the same way, before hwi runs, where it sent it to the device; and it refuses a request that is no Psbt with a BTClibTypeError rather than an AttributeError.

psbt.combine refuses with a BTClibValueError psbts whose merge Psbt.assert_valid refuses, where it returned that psbt; drop from the copies the field that contradicts the others – a witness_utxo differing from another copy’s non_witness_utxo output – before combining. It refuses with a BTClibTypeError a psbts that is not a sequence – a generator, an iterator – where it read it; pass a list or a tuple.

psbt.join refuses with a BTClibTypeError a psbts that is not a sequence – a dict view, any container that is only iterable – where it joined it; pass a list or a tuple.

descriptors.KeyExpression refuses with a BTClibValueError an extended private key as xkey, where it held it; pass its xpub, and the xprv under that xpub in prv_keys where a hardened step needs it.

descriptors.KeyExpression’s xkey holds an extended public key of a version btclib decodes, a wallet-policy placeholder wallet_policy writes – @N, @N/**, @N/<M;N>/*, musig(@N,...) with one of those suffixes, K – or nothing. Any other str is refused with a BTClibValueError and any other type with a BTClibTypeError, where they were held; pass an xpub.

silent_payments.keys_from_address refuses with a BTClibValueError a mixed-case address and one holding a character outside ASCII, where it read the address the string lowers to; pass the address all lower case or all upper case. For such a spelling of an address a wallet handed out, address in wallet answers False and Wallet.address_info raises, where both found the address.

silent_payments.keys_from_address and silent_payments.output_keys, which reads each address through it, BIP32KeyData.b58decode and what reads an extended key through it, bip32.derive among them, bip322.Sig.b64decode, bip322.verify and assert_as_valid for the signature, Psbt.b64decode, tx_or_psbt.tx_or_psbt_from_any, BIP32KeyOrigin.from_description and bip32.str_from_der_path’s fingerprint strip only space, tab, newline, carriage return, vertical tab and form feed. Text padded with U+00A0, U+3000, U+2028, U+0085 or another character outside ASCII that str.isspace counts is refused with a BTClibValueError, where it was read as the value it wraps, and so is text padded with U+001C to U+001F. For such a spelling of an address a wallet handed out, address in wallet answers False and Wallet.address_info raises. Strip that padding before passing the text.

mnemonic.entropy.bin_str_entropy_from_int refuses with a BTClibValueError a string entropy that is not ASCII digits – binary after 0b, hex after 0x, decimal otherwise – once space, tab, newline, carriage return, vertical tab and form feed are stripped around it, where int read 0x1_0, 1_6, +16 and fullwidth or Arabic-Indic digits as 16 and U+00A0 or U+3000 was stripped too. The functions reading a binary-string entropy, bin_str_entropy_from_str, bytes_entropy_from_str and bin_str_entropy_from_entropy among them, refuse one holding anything but ASCII 0 and 1 – a sign, 0b, whitespace, an underscore – which int read as binary; bytes_entropy_from_str raised a bare OverflowError on a leading -. Pass ASCII digits alone. The refusals read invalid entropy: what follows 0x is not ASCII hex digits and invalid entropy: not ASCII decimal digits, where they said not a base 16 number and not a base 10 number, and a negative entropy’s reads negative entropy, without the number. bin_str_entropy_from_wordlist_indexes and bin_str_entropy_from_rolls name the range and not the value of an index or a roll outside it: invalid index: not in [0, 2048), invalid roll: not in [1-6]. Match invalid entropy, negative entropy, invalid index and invalid roll alone.

descriptors.parse refuses with a BTClibValueError a bare multi() or sortedmulti() of more than three keys, one inside sh() whose redeem script is over 520 bytes – sixteen compressed keys are 547 – and one of more than twenty keys anywhere, where it parsed them; put the keys of the first two inside wsh() or sh(wsh()) instead. wallet_policy_descriptor refuses the same, the descriptor it builds being read by parse.

Psbt.b64decode refuses with a BTClibValueError text holding a character outside the base64 alphabet – a line break, a space, U+001C, ! – anywhere past the ASCII whitespace it strips from either end, where it decoded the psbt the rest of the text spells. Remove the line breaks from a wrapped psbt, or pass it to tx_or_psbt.tx_or_psbt_from_any, which removes them.

bip32.indexes_from_der_path, hardenings_from_der_path, int_from_index_str and every function reading a path string through them, bip32.derive and BIP32KeyOrigin.from_description among them, refuse with a BTClibValueError a step whose number is not ASCII decimal digits – 1_0, +1, -0, fullwidth or Arabic-Indic digits – or that is padded with anything but space, tab, newline, carriage return, vertical tab and form feed, where it was read as the number. With bip380_enforced=True they refuse a step padded at all, and so descriptors.parse refuses a step with a space around it. BIP32KeyOrigin.from_description refuses text whose ninth character is not /, where it dropped that character and read deadbeef0/1 as deadbeef/1. int_from_index_str refuses a step that is not a str with a BTClibTypeError, and reads a step with ASCII whitespace after its symbol, "0h ", which it refused. Pass each step as ASCII digits. The refusals read invalid derivation index: not ASCII decimal digits and invalid index: not below 2**31, where they quoted the step or its number; match those. bip85’s functions write their index into a path string, and refuse a negative one with the first and one of 2**31 or more with the second.

descriptors.parse and miniscript.parse refuse with a BTClibValueError a / followed by no path step, in a key origin, after an extended key or after a musig() – [deadbeef/], xpub.../, xpub...//*, musig(...)/ – where they read the key expression as though that / were absent. Remove the /; the refusal reads invalid derivation index. BIP32KeyOrigin.from_description, whose reading of a path is the lenient one, reads deadbeef/ as deadbeef.

Psbt.b64decode, bip322.Sig.b64decode, and bip322.verify and assert_as_valid for the signature, refuse with a BTClibValueError base64 that is not the text base64.b64encode writes for its bytes, where they read those bytes: a bit set in what the last character leaves over, YR== read as YQ==, and on CPython and PyPy 3.11 an = after a whole group. The refusal reads invalid base64 encoding: not canonical, and bip322.verify raises it for a legacy signature so encoded, where it answered False. tx_or_psbt.tx_or_psbt_from_any refuses such a str as neither hex nor base64, and reads such bytes as a raw transaction, where both were read as the psbt. Pass the text base64.b64encode writes, which is the text Bitcoin Core writes and the only text it reads.

A refusal in mnemonic no longer quotes secret material, so its text changed: an unknown word reads unknown '<lang>' word at position <n>, where it was unknown '<lang>' word: '<word>', and a SLIP-0039 one not in the SLIP-0039 word-list: words at positions [<n>, ...]. A BIP39 checksum reads invalid checksum: <n> words, a SLIP-0039 padding invalid padding: must be all zeros, and an unrecognized Electrum sentence unknown electrum mnemonic version; not in [...]. Match the new text if you match these. mnemonic.mnemonic_from_indexes refuses an index outside the word-list, a negative one included, which read from the end of the list; pass indexes in [0, len(wordlist)). WordLists.langs_of_words refuses a lone str, which it read one character at a time; pass the list of words.

mnemonic.mnemonic_from_indexes and WordLists.langs_of_words refuse with a BTClibTypeError an iterable that is not a sequence – a generator, a set, an iterator – where they read it; pass a list or a tuple. mnemonic_from_indexes refuses too an index that is a bool or an integer of a type other than int, numpy.int64 or an object with __index__ included, where it read it; pass ints. dispatch.seed_type_from_mnemonic and all_seed_types_from_mnemonic refuse a lang that is not a str with a BTClibTypeError, where they answered "" and []; pass the language code.

The BIP39, SLIP39 and Electrum mnemonic schemes are btclib-mnemonics’, a dependency of this package, and btclib_wallet.mnemonic keeps what builds a key from their seed: bip39.mxprv_from_mnemonic, slip39.mxprv_from_mnemonics, electrum.mxprv_from_mnemonic and electrum.old_master_pub_key_from_mnemonic. Every path below stops resolving, and nothing re-exports it; import the name from btclib_mnemonics instead. A refusal those four functions pass on from btclib_mnemonics, of a mnemonic, a share or a passphrase, is that package’s own BTClibMnemonicsTypeError or BTClibMnemonicsValueError, a TypeError or a ValueError and no BTClibException, where it was a BTClibTypeError or a BTClibValueError; catch TypeError and ValueError. electrum.mxprv_from_mnemonic still refuses a 2fa, a 2fa_segwit or an old mnemonic with its own BTClibValueError, and for an old one that refusal now ends use old_master_pub_key_from_mnemonic, where it named old_master_prv_key_from_mnemonic. The paragraphs above that name a function that moved describe it as it is under btclib_mnemonics, where each BTClibTypeError or BTClibValueError they name for it is a BTClibMnemonicsTypeError or a BTClibMnemonicsValueError, and where the entropy conversions they name are private.

  • btclib_wallet.mnemonic.dispatch is btclib_mnemonics.dispatch.

  • btclib_wallet.mnemonic.entropy is btclib_mnemonics.entropy, less the functions the bullets below make private.

  • btclib_wallet.mnemonic.mnemonic is btclib_mnemonics.mnemonic.

  • btclib_wallet.mnemonic’s BinStr is btclib_mnemonics.entropy.BinStr.

  • btclib_wallet.mnemonic’s Entropy is btclib_mnemonics.entropy.Entropy.

  • btclib_wallet.mnemonic’s bin_str_entropy_from_random is btclib_mnemonics.entropy.bin_str_entropy_from_random.

  • btclib_wallet.mnemonic’s bin_str_entropy_from_rolls is btclib_mnemonics.entropy.bin_str_entropy_from_rolls.

  • btclib_wallet.mnemonic’s collect_rolls is btclib_mnemonics.entropy.collect_rolls.

  • btclib_wallet.mnemonic’s Mnemonic is btclib_mnemonics.mnemonic.Mnemonic.

  • btclib_wallet.mnemonic’s WORDLISTS is btclib_mnemonics.mnemonic.WORDLISTS.

  • btclib_wallet.mnemonic’s indexes_from_mnemonic is btclib_mnemonics.mnemonic.indexes_from_mnemonic.

  • btclib_wallet.mnemonic’s mnemonic_from_indexes is btclib_mnemonics.mnemonic.mnemonic_from_indexes.

  • btclib_wallet.mnemonic’s normalize_mnemonic is btclib_mnemonics.mnemonic.normalize_mnemonic.

  • btclib_wallet.mnemonic’s bin_str_entropy_from_bytes is gone, private in btclib_mnemonics: pass the bytes to the scheme’s mnemonic_from_entropy, which reads them.

  • btclib_wallet.mnemonic’s bin_str_entropy_from_entropy is gone, private in btclib_mnemonics: pass the entropy to the scheme’s mnemonic_from_entropy, which reads it.

  • btclib_wallet.mnemonic’s bin_str_entropy_from_int is gone, private in btclib_mnemonics: pass the integer to the scheme’s mnemonic_from_entropy, which reads it.

  • btclib_wallet.mnemonic’s bin_str_entropy_from_str is gone, private in btclib_mnemonics: pass the binary string itself.

  • btclib_wallet.mnemonic’s bin_str_entropy_from_wordlist_indexes is gone, private in btclib_mnemonics: use btclib_mnemonics.mnemonic.mnemonic_from_indexes and the scheme’s entropy_from_mnemonic.

  • btclib_wallet.mnemonic’s bytes_entropy_from_str is gone, private in btclib_mnemonics: int(entropy, 2).to_bytes(len(entropy) // 8, "big") is the conversion.

  • btclib_wallet.mnemonic’s wordlist_indexes_from_bin_str_entropy is gone, private in btclib_mnemonics: use the scheme’s mnemonic_from_entropy and btclib_mnemonics.mnemonic.indexes_from_mnemonic.

  • btclib_wallet.mnemonic.bip39’s entropy_from_mnemonic is btclib_mnemonics.bip39.entropy_from_mnemonic.

  • btclib_wallet.mnemonic.bip39’s lang_from_mnemonic is btclib_mnemonics.bip39.lang_from_mnemonic.

  • btclib_wallet.mnemonic.bip39’s mnemonic_from_entropy is btclib_mnemonics.bip39.mnemonic_from_entropy.

  • btclib_wallet.mnemonic.bip39’s seed_from_mnemonic is btclib_mnemonics.bip39.seed_from_mnemonic.

  • btclib_wallet.mnemonic.electrum’s ELECTRUM_WORDLISTS is btclib_mnemonics.electrum.ELECTRUM_WORDLISTS.

  • btclib_wallet.mnemonic.electrum’s entropy_from_mnemonic is btclib_mnemonics.electrum.entropy_from_mnemonic.

  • btclib_wallet.mnemonic.electrum’s hex_seed_from_old_mnemonic is btclib_mnemonics.electrum.hex_seed_from_old_mnemonic.

  • btclib_wallet.mnemonic.electrum’s lang_from_mnemonic is btclib_mnemonics.electrum.lang_from_mnemonic.

  • btclib_wallet.mnemonic.electrum’s mnemonic_from_entropy is btclib_mnemonics.electrum.mnemonic_from_entropy.

  • btclib_wallet.mnemonic.electrum’s old_master_prv_key_from_mnemonic is btclib_mnemonics.electrum.old_master_prv_key_from_mnemonic.

  • btclib_wallet.mnemonic.electrum’s old_mnemonic_from_hex_seed is btclib_mnemonics.electrum.old_mnemonic_from_hex_seed.

  • btclib_wallet.mnemonic.electrum’s version_from_mnemonic is btclib_mnemonics.electrum.version_from_mnemonic.

  • btclib_wallet.mnemonic.slip39’s Share is btclib_mnemonics.slip39.Share.

  • btclib_wallet.mnemonic.slip39’s master_secret_from_mnemonics is btclib_mnemonics.slip39.master_secret_from_mnemonics.

  • btclib_wallet.mnemonic.slip39’s mnemonic_from_share is btclib_mnemonics.slip39.mnemonic_from_share.

  • btclib_wallet.mnemonic.slip39’s mnemonics_from_master_secret is btclib_mnemonics.slip39.mnemonics_from_master_secret.

  • btclib_wallet.mnemonic.slip39’s share_from_mnemonic is btclib_mnemonics.slip39.share_from_mnemonic.

The bound on a timeout that fetch.ElectrumFetcher refuses is fetch.transport.MAX_TIMEOUT, 2147483 seconds, where the paragraph above names threading.TIMEOUT_MAX; pass at most that many seconds.

fetch.EsploraFetcher refuses at construction, with a BTClibTypeError or a BTClibValueError, a timeout that is no number, a bool, not positive, or above fetch.transport.MAX_TIMEOUT, 2147483 seconds, where its first fetch raised a FetchError and a timeout above that bound reached a transport of the caller’s. fetch.transport.TlsLineTransport refuses a timeout above that bound with a BTClibValueError, where it waited with one up to what the platform’s socket took and failed past it, with an OverflowError or, where the socket’s wait refused it, a FetchError; its refusals read non-numeric timeout: <type> and timeout is not a positive number of seconds up to 2147483, where they quoted the value. Pass a positive number of seconds up to that bound.

CHANGELOG.md’s own v2026.9.30 section has the rest.

v2026.9.24

The first release of btclib-wallet, whose modules leave btclib (issue btclib-org/btclib#2129). A caller importing one of them from btclib installs this package and imports it from btclib_wallet instead, the path below the package unchanged: btclib.bip32 becomes btclib_wallet.bip32, btclib.mnemonic.bip39 becomes btclib_wallet.mnemonic.bip39.

CHANGELOG.md’s own v2026.9.24 section has the rest.