SuiteScript Item Fulfillments: Why Dynamic Mode Matters for Kit Items
I ran into this recently while writing a user event script that needed to selectively fulfill lines on an item fulfillment. The logic was straightforward - transform from a sales order, loop through the lines, set itemreceive and quantities, save.
Except NetSuite kept throwing this:
THE_KIT_DEFINITION_HAS_BEEN_CHANGED_PLEASE_USE_THE_UPDATE_ORDERS_KIT_MEMBERS
_WITH_CURRENT_KIT_MEMBERS_MASS_UPDATE_IN_ORDER_TO_FULFILL_THIS_ORDER
Or in plain English: “The kit definition has been changed. Please use the Update Orders Kit Members with Current Kit Members Mass Update in order to fulfill this order.”
The kit definition hadn’t changed. The script wasn’t touching the kit structure at all - just setting which lines to fulfill and in what quantity. But the error was consistent and the stack trace pointed nowhere useful.
I ended up opening a case with NetSuite support, and their answer was straightforward: the script needs to run in dynamic mode.
Standard vs. dynamic mode
In SuiteScript, when you load or create a record, you choose between two modes:
// Standard mode (default)
const rec = record.load({
type: record.Type.ITEM_FULFILLMENT,
id: fulfillmentId
});
// Dynamic mode
const rec = record.load({
type: record.Type.ITEM_FULFILLMENT,
id: fulfillmentId,
isDynamic: true
});
The difference matters more than it looks.
Standard mode queues up all your changes and commits them together when you call save(). You can set values on any line in any order - nothing gets recalculated until the end.
Dynamic mode processes changes immediately as you make them, the same way the UI does. When you select a line and set a value, NetSuite recalculates dependent fields right then.
For most record types, either mode works. But item fulfillments with kit items are different.
Why kits break in standard mode
Kit items in NetSuite are parent-child structures. When you fulfill a sales order that contains a kit, the fulfillment record has both the kit parent line and its component lines. Those component lines are dynamically generated based on the kit definition.
In the UI, item fulfillments run in dynamic mode. NetSuite recalculates the kit structure in real time as you check lines, change quantities, etc. When you use standard mode in a script, that real-time recalculation doesn’t happen. NetSuite queues everything up, and by the time it tries to process the save, the state of the kit lines doesn’t match what it expects.
That’s where THE_KIT_DEFINITION_HAS_BEEN_CHANGED comes from. The kit definition is fine - the record just thinks it changed because the component lines weren’t recalculated as you modified them.
The fix
Switch to dynamic mode and replace setSublistValue with the dynamic mode equivalents: selectLine, setCurrentSublistValue, and commitLine.
Here’s what the pattern looks like for selectively fulfilling lines:
const itemFulfillment = record.transform({
fromType: record.Type.SALES_ORDER,
fromId: salesOrderId,
toType: record.Type.ITEM_FULFILLMENT,
isDynamic: true
});
const lineCount = itemFulfillment.getLineCount({ sublistId: 'item' });
// First pass: uncheck all lines
for (let i = 0; i < lineCount; i++) {
itemFulfillment.selectLine({ sublistId: 'item', line: i });
itemFulfillment.setCurrentSublistValue({
sublistId: 'item',
fieldId: 'itemreceive',
value: false
});
itemFulfillment.commitLine({ sublistId: 'item' });
}
// Second pass: check and set quantities for the lines you want to fulfill
for (let i = 0; i < lineCount; i++) {
const itemId = itemFulfillment.getSublistValue({
sublistId: 'item',
fieldId: 'item',
line: i
});
const quantityToFulfill = getQuantityForItem(itemId); // your logic here
if (quantityToFulfill > 0) {
itemFulfillment.selectLine({ sublistId: 'item', line: i });
itemFulfillment.setCurrentSublistValue({
sublistId: 'item',
fieldId: 'itemreceive',
value: true
});
itemFulfillment.setCurrentSublistValue({
sublistId: 'item',
fieldId: 'quantity',
value: quantityToFulfill
});
itemFulfillment.commitLine({ sublistId: 'item' });
}
}
itemFulfillment.save();
The pattern is: selectLine → setCurrentSublistValue → commitLine. Every line change gets processed immediately, and NetSuite keeps the kit structure in sync the same way it does in the UI.
When to use which mode
This isn’t just a kit item thing. Dynamic mode is generally safer for any record type where NetSuite has dependencies between fields or lines:
- Item fulfillments with kit or assembly items
- Item receipts with lot or serial number tracking
- Sales orders with pricing that depends on quantity or customer
- Inventory adjustments with bin numbers
Standard mode is fine for simpler operations - setting header fields, modifying straightforward sublists, or bulk updates where performance matters (standard mode is faster since it skips the real-time recalculations).
If you’re getting unexpected errors on a record type and the logic looks correct, check which mode you’re using. That’s been the root cause more times than I can count.