diff --git a/modules/ecash-agora/src/agora.ts b/modules/ecash-agora/src/agora.ts
--- a/modules/ecash-agora/src/agora.ts
+++ b/modules/ecash-agora/src/agora.ts
@@ -140,9 +140,12 @@
         dustAmount?: number;
         /** Fee per kB to use when building the tx. */
         feePerKb?: number;
+        /**  Allow accepting an offer such that the remaining quantity is unacceptable */
+        allowUnspendable?: boolean;
     }): Tx {
         const dustAmount = params.dustAmount ?? DEFAULT_DUST_LIMIT;
         const feePerKb = params.feePerKb ?? DEFAULT_FEE_PER_KB;
+        const allowUnspendable = params.allowUnspendable ?? false;
         const txBuild = this._acceptTxBuilder({
             covenantSk: params.covenantSk,
             covenantPk: params.covenantPk,
@@ -155,6 +158,7 @@
                 params.recipientScript,
             ],
             acceptedTokens: params.acceptedTokens,
+            allowUnspendable,
         });
         return txBuild.sign(params.ecc, feePerKb, dustAmount);
     }
@@ -197,6 +201,7 @@
         fuelInputs: TxBuilderInput[];
         extraOutputs: TxBuilderOutput[];
         acceptedTokens?: bigint;
+        allowUnspendable?: boolean;
     }): TxBuilder {
         switch (this.variant.type) {
             case 'ONESHOT':
@@ -232,6 +237,17 @@
                         `Must acceptedTokens must be a multiple of ${truncFactor}`,
                     );
                 }
+
+                if (
+                    params.allowUnspendable === false ||
+                    typeof params.allowUnspendable === 'undefined'
+                ) {
+                    // Prevent creation of unacceptable offer
+                    agoraPartial.preventUnacceptableRemainder(
+                        params.acceptedTokens,
+                    );
+                }
+
                 txBuild.inputs.push({
                     input: this.txBuilderInput,
                     signatory: AgoraPartialSignatory(
diff --git a/modules/ecash-agora/src/partial.ts b/modules/ecash-agora/src/partial.ts
--- a/modules/ecash-agora/src/partial.ts
+++ b/modules/ecash-agora/src/partial.ts
@@ -547,6 +547,48 @@
         return askedTruncSats << numSatsTruncBits;
     }
 
+    /**
+     * Throw an error if accept amount is invalid
+     * Note we do not prepare amounts in this function
+     * @param acceptedTokens
+     */
+    public preventUnacceptableRemainder(acceptedTokens: bigint) {
+        // Validation to avoid creating an offer that cannot be accepted
+        //
+        // 1 - confirm the remaining offer amount is more than the
+        //     min accept amount for this agora partial
+        //
+        // 2 - Confirm the cost of accepting the (full) remainder is
+        //     at least dust. This is already confirmed...for offers
+        //     created by this lib... as minAcceptedTokens() must
+        //     cost more than dust
+        //
+        //
+        // If these condtions are not met, an AgoraOffer would be created
+        // that is impossible to accept; can only be canceld by its maker
+
+        // Get the token qty that would remain after this accept
+        const offeredTokens = this.offeredTokens();
+        const remainingTokens = offeredTokens - acceptedTokens;
+        if (remainingTokens <= 0n) {
+            return;
+        }
+        // Full accepts are always ok
+
+        const minAcceptedTokens = this.minAcceptedTokens();
+        const priceOfRemainingTokens = this.askedSats(remainingTokens);
+        if (remainingTokens < minAcceptedTokens) {
+            throw new Error(
+                `Accepting ${acceptedTokens} token satoshis would leave an amount lower than the min acceptable by the terms of this contract, and hence unacceptable. Accept fewer tokens or the full offer.`,
+            );
+        }
+        if (priceOfRemainingTokens < this.dustAmount) {
+            throw new Error(
+                `Accepting ${acceptedTokens} token satoshis would leave an amount priced lower than dust. Accept fewer tokens or the full offer.`,
+            );
+        }
+    }
+
     /**
      * Prepare the given acceptedTokens amount for the Script; `acceptedTokens`
      * must have the lowest numTokenTruncBytes bytes set to 0 and this function
diff --git a/modules/ecash-agora/tests/partial-helper-alp.ts b/modules/ecash-agora/tests/partial-helper-alp.ts
--- a/modules/ecash-agora/tests/partial-helper-alp.ts
+++ b/modules/ecash-agora/tests/partial-helper-alp.ts
@@ -132,6 +132,7 @@
     takerSk: Uint8Array;
     takerInput: TxBuilderInput;
     acceptedTokens: bigint;
+    allowUnspendable?: boolean;
 }) {
     const takerSk = params.takerSk;
     const takerPk = params.ecc.derivePubkey(takerSk);
@@ -144,6 +145,7 @@
         fuelInputs: [params.takerInput],
         recipientScript: takerP2pkh,
         acceptedTokens: params.acceptedTokens,
+        allowUnspendable: params.allowUnspendable,
     });
     const acceptTxid = (await params.chronik.broadcastTx(acceptTx.ser())).txid;
     return acceptTxid;
diff --git a/modules/ecash-agora/tests/partial-helper-slp.ts b/modules/ecash-agora/tests/partial-helper-slp.ts
--- a/modules/ecash-agora/tests/partial-helper-slp.ts
+++ b/modules/ecash-agora/tests/partial-helper-slp.ts
@@ -136,6 +136,7 @@
     takerSk: Uint8Array;
     takerInput: TxBuilderInput;
     acceptedTokens: bigint;
+    allowUnspendable?: boolean;
 }) {
     const takerSk = params.takerSk;
     const takerPk = params.ecc.derivePubkey(takerSk);
@@ -148,6 +149,7 @@
         fuelInputs: [params.takerInput],
         recipientScript: takerP2pkh,
         acceptedTokens: params.acceptedTokens,
+        allowUnspendable: params.allowUnspendable,
     });
     const acceptTxid = (await params.chronik.broadcastTx(acceptTx.ser())).txid;
     return acceptTxid;
diff --git a/modules/ecash-agora/tests/partial.alp.test.ts b/modules/ecash-agora/tests/partial.alp.test.ts
--- a/modules/ecash-agora/tests/partial.alp.test.ts
+++ b/modules/ecash-agora/tests/partial.alp.test.ts
@@ -2,7 +2,7 @@
 // Distributed under the MIT software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 
-import { expect, use } from 'chai';
+import { expect, use, assert } from 'chai';
 import chaiAsPromised from 'chai-as-promised';
 import { ChronikClient } from 'chronik-client';
 import {
@@ -103,6 +103,7 @@
         priceNanoSatsPerToken: bigint;
         acceptedTokens: bigint;
         askedSats: number;
+        allowUnspendable?: boolean;
     }
     const TEST_CASES: TestCase[] = [
         {
@@ -118,6 +119,7 @@
             priceNanoSatsPerToken: 1000000000n,
             acceptedTokens: 546n,
             askedSats: 546,
+            allowUnspendable: true,
         },
         {
             offeredTokens: 1000n,
@@ -460,6 +462,7 @@
                 offer,
                 takerInput,
                 acceptedTokens: testCase.acceptedTokens,
+                allowUnspendable: testCase.allowUnspendable,
             });
             const acceptTx = await chronik.tx(acceptTxid);
             const offeredTokens = agoraPartial.offeredTokens();
@@ -601,4 +604,117 @@
             });
         });
     }
+    it('Without manually setting an over-ride, we are unable to accept an agora partial if the remaining offer would be unacceptable due to the terms of the contract', async () => {
+        const thisTestCase: TestCase = {
+            offeredTokens: 1000n,
+            info: '1sat/token, dust accept',
+            priceNanoSatsPerToken: 1000000000n,
+            acceptedTokens: 546n,
+            askedSats: 546,
+            allowUnspendable: true,
+        };
+        const agora = new Agora(chronik);
+        const agoraPartial = await agora.selectParams({
+            offeredTokens: thisTestCase.offeredTokens,
+            priceNanoSatsPerToken: thisTestCase.priceNanoSatsPerToken,
+            minAcceptedTokens: thisTestCase.acceptedTokens,
+            makerPk,
+            ...BASE_PARAMS_ALP,
+        });
+        const askedSats = agoraPartial.askedSats(thisTestCase.acceptedTokens);
+        const requiredSats = askedSats + 2000n;
+        const [fuelInput, takerInput] = await makeBuilderInputs([
+            4000,
+            Number(requiredSats),
+        ]);
+
+        const offer = await makeAlpOffer({
+            chronik,
+            ecc,
+            agoraPartial,
+            makerSk,
+            fuelInput,
+        });
+
+        const expectedError = `Accepting ${thisTestCase.acceptedTokens} token satoshis would leave an amount lower than the min acceptable by the terms of this contract, and hence unacceptable. Accept fewer tokens or the full offer.`;
+
+        // We can get the error from the isolated method
+        expect(() =>
+            agoraPartial.preventUnacceptableRemainder(
+                thisTestCase.acceptedTokens,
+            ),
+        ).to.throw(Error, expectedError);
+
+        // We get an error for test cases that would result in unspendable amounts
+        // if we do not pass allowUnspendable to agoraOffer.acceptTx
+        await assert.isRejected(
+            takeAlpOffer({
+                chronik,
+                ecc,
+                takerSk,
+                offer,
+                takerInput,
+                acceptedTokens: thisTestCase.acceptedTokens,
+                allowUnspendable: false,
+            }),
+            expectedError,
+        );
+    });
+    it('Without manually setting an over-ride, we are unable to accept an agora partial if the remaining offer would be unacceptable due to a price less than dust', async () => {
+        // ecash-agora does not support creating an agora partial with min accept amount priced less than dust
+        // from the approximateParams method
+        // However we can still do this if we manually create a new AgoraPartial
+        // I think it is okay to preserve this, as the protocol does technically allow it,
+        // and perhaps a power user wants to do this for some reason
+
+        // Manually build an offer equivalent to previous test but accepting 500 tokens
+        const agoraPartial = new AgoraPartial({
+            truncTokens: 1000n,
+            numTokenTruncBytes: 0,
+            tokenScaleFactor: 2145336n,
+            scaledTruncTokensPerTruncSat: 2145336n,
+            numSatsTruncBytes: 0,
+            minAcceptedScaledTruncTokens: 1072668000n,
+            scriptLen: 209,
+            enforcedLockTime: 1333546081,
+            makerPk,
+            ...BASE_PARAMS_ALP,
+        });
+        const acceptedTokens = 500n;
+        const askedSats = agoraPartial.askedSats(acceptedTokens);
+        const requiredSats = askedSats + 2000n;
+        const [fuelInput, takerInput] = await makeBuilderInputs([
+            4000,
+            Number(requiredSats),
+        ]);
+
+        const offer = await makeAlpOffer({
+            chronik,
+            ecc,
+            agoraPartial,
+            makerSk,
+            fuelInput,
+        });
+
+        const expectedError = `Accepting 500 token satoshis would leave an amount priced lower than dust. Accept fewer tokens or the full offer.`;
+
+        // We can get the error from the isolated method
+        expect(() =>
+            agoraPartial.preventUnacceptableRemainder(acceptedTokens),
+        ).to.throw(Error, expectedError);
+
+        // And from attempting to accept
+        await assert.isRejected(
+            takeAlpOffer({
+                chronik,
+                ecc,
+                takerSk,
+                offer,
+                takerInput,
+                acceptedTokens: acceptedTokens,
+                allowUnspendable: false,
+            }),
+            expectedError,
+        );
+    });
 });
diff --git a/modules/ecash-agora/tests/partial.slp.test.ts b/modules/ecash-agora/tests/partial.slp.test.ts
--- a/modules/ecash-agora/tests/partial.slp.test.ts
+++ b/modules/ecash-agora/tests/partial.slp.test.ts
@@ -2,7 +2,7 @@
 // Distributed under the MIT software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 
-import { expect, use } from 'chai';
+import { expect, use, assert } from 'chai';
 import chaiAsPromised from 'chai-as-promised';
 import { ChronikClient } from 'chronik-client';
 import {
@@ -102,6 +102,7 @@
         priceNanoSatsPerToken: bigint;
         acceptedTokens: bigint;
         askedSats: number;
+        allowUnspendable?: boolean;
     }
     const TEST_CASES: TestCase[] = [
         {
@@ -113,10 +114,11 @@
         },
         {
             offeredTokens: 1000n,
-            info: '1sat/token, dust accept',
+            info: '1sat/token, dust accept, must allowUnspendable',
             priceNanoSatsPerToken: 1000000000n,
             acceptedTokens: 546n,
             askedSats: 546,
+            allowUnspendable: true,
         },
         {
             offeredTokens: 1000n,
@@ -515,6 +517,7 @@
                 offer,
                 takerInput,
                 acceptedTokens: testCase.acceptedTokens,
+                allowUnspendable: testCase.allowUnspendable,
             });
             const acceptTx = await chronik.tx(acceptTxid);
             const offeredTokens = agoraPartial.offeredTokens();
@@ -645,4 +648,146 @@
             });
         });
     }
+
+    it('Without manually setting an over-ride, we are unable to accept an agora partial if the remaining offer would be unacceptable due to the terms of the contract', async () => {
+        const thisTestCase: TestCase = {
+            offeredTokens: 1000n,
+            info: '1sat/token, dust accept, must allowUnspendable',
+            priceNanoSatsPerToken: 1000000000n,
+            acceptedTokens: 546n,
+            askedSats: 546,
+            allowUnspendable: true,
+        };
+        const agora = new Agora(chronik);
+        const agoraPartial = await agora.selectParams({
+            offeredTokens: thisTestCase.offeredTokens,
+            priceNanoSatsPerToken: thisTestCase.priceNanoSatsPerToken,
+            minAcceptedTokens: thisTestCase.acceptedTokens,
+            makerPk,
+            ...BASE_PARAMS_SLP,
+        });
+        const askedSats = agoraPartial.askedSats(thisTestCase.acceptedTokens);
+        const requiredSats = askedSats + 2000n;
+        const [fuelInput, takerInput] = await makeBuilderInputs([
+            4000,
+            Number(requiredSats),
+        ]);
+
+        const offer = await makeSlpOffer({
+            chronik,
+            ecc,
+            agoraPartial,
+            makerSk,
+            fuelInput,
+        });
+
+        const expectedError =
+            'Accepting 546 token satoshis would leave an amount lower than the min acceptable by the terms of this contract, and hence unacceptable. Accept fewer tokens or the full offer.';
+
+        // We can get the error from this isolated method
+        expect(() =>
+            agoraPartial.preventUnacceptableRemainder(
+                thisTestCase.acceptedTokens,
+            ),
+        ).to.throw(Error, expectedError);
+
+        // Or by attempting to accept the offer
+        await assert.isRejected(
+            takeSlpOffer({
+                chronik,
+                ecc,
+                takerSk,
+                offer,
+                takerInput,
+                acceptedTokens: thisTestCase.acceptedTokens,
+                allowUnspendable: false,
+            }),
+            expectedError,
+        );
+    });
+    it('Without manually setting an over-ride, we are unable to accept an agora partial if the remaining offer would be unacceptable due to a price less than dust', async () => {
+        // ecash-agora does not support creating an agora partial with min accept amount priced less than dust
+        // from the approximateParams method
+        // However we can still do this if we manually create a new AgoraPartial
+        // I think it is okay to preserve this, as the protocol does technically allow it,
+        // and perhaps a power user wants to do this for some reason
+
+        // Manually build an offer equivalent to previous test but accepting 500 tokens
+        const agoraPartial = new AgoraPartial({
+            truncTokens: 1000n,
+            numTokenTruncBytes: 0,
+            tokenScaleFactor: 2145336n,
+            scaledTruncTokensPerTruncSat: 2145336n,
+            numSatsTruncBytes: 0,
+            minAcceptedScaledTruncTokens: 1072668000n,
+            scriptLen: 216,
+            enforcedLockTime: 1087959628,
+            makerPk,
+            ...BASE_PARAMS_SLP,
+        });
+        const acceptedTokens = 500n;
+        const askedSats = agoraPartial.askedSats(acceptedTokens);
+        const requiredSats = askedSats + 2000n;
+        const [fuelInput, takerInput] = await makeBuilderInputs([
+            4000,
+            Number(requiredSats),
+        ]);
+
+        const offer = await makeSlpOffer({
+            chronik,
+            ecc,
+            agoraPartial,
+            makerSk,
+            fuelInput,
+        });
+
+        const expectedError =
+            'Accepting 500 token satoshis would leave an amount priced lower than dust. Accept fewer tokens or the full offer.';
+
+        // We can get the error from this isolated method
+        expect(() =>
+            agoraPartial.preventUnacceptableRemainder(acceptedTokens),
+        ).to.throw(Error, expectedError);
+
+        // Or by attempting to accept the offer
+        await assert.isRejected(
+            takeSlpOffer({
+                chronik,
+                ecc,
+                takerSk,
+                offer,
+                takerInput,
+                acceptedTokens: acceptedTokens,
+                allowUnspendable: false,
+            }),
+            expectedError,
+        );
+
+        // We also check if allowUnspendable is specified as undefined
+        await assert.isRejected(
+            takeSlpOffer({
+                chronik,
+                ecc,
+                takerSk,
+                offer,
+                takerInput,
+                acceptedTokens: acceptedTokens,
+                allowUnspendable: undefined,
+            }),
+            expectedError,
+        );
+
+        // And if the user simply omits the allowUnspendable param
+        await assert.isRejected(
+            takeSlpOffer({
+                chronik,
+                ecc,
+                takerSk,
+                offer,
+                takerInput,
+                acceptedTokens: acceptedTokens,
+            }),
+            expectedError,
+        );
+    });
 });