diff --git a/tools/server/server-context.cpp b/tools/server/server-context.cpp
index 01e7eaa35..c2f29094e 100644
--- a/tools/server/server-context.cpp
+++ b/tools/server/server-context.cpp
@@ -321,6 +321,9 @@ struct server_slot {
return;
}
SLT_WRN(*this, "dropping an unfinished splice at %d tokens\n", prompt.n_tokens());
+ if (!common_speculative_flush(spec, id)) {
+ SLT_ERR(*this, "%s", "failed to flush the draft context before aborting SCR\n");
+ }
scr.reset();
llama_scr_set_spliced(ctx_tgt, id, false);
if (ctx_dft) {
@@ -2869,7 +2872,13 @@ private:
// the prefill of a splicing slot has reached its next run: the run's tokens are already in the cache. after the
// last run the recurrent state continues from the pre-edit snapshot, and the splice is done
- void scr_take_run(server_slot & slot) {
+ bool scr_take_run(server_slot & slot) {
+ // Short edited gaps can leave MTP catch-up rows pending below the relocated KV.
+ // Decode them while the splice guard is still active, before skipping this run.
+ if (!common_speculative_flush(spec.get(), slot.id)) {
+ return false;
+ }
+
auto & s = *slot.scr;
const auto & tokens = slot.task->tokens;
@@ -2897,6 +2906,7 @@ private:
}
llama_scr_recr_set_pos(ctx_tgt, slot.id, slot.prompt.tokens.pos_next() - 1);
+ return true;
}
// n_tokens_cur: the number of tokens added to the batch for the current slot
@@ -4397,7 +4407,11 @@ private:
if (batch.size() > n_tokens_prev) {
break;
}
- scr_take_run(slot);
+ if (!scr_take_run(slot)) {
+ send_error(slot, "failed to flush the draft context before an SCR run", ERROR_TYPE_SERVER);
+ slot.release();
+ return;
+ }
continue;
}