windmill-labs/windmill · error

No actual user message found to restart from

Error message

No actual user message found to restart from

What it means

During restart-from-message, the transcript index is mapped to the underlying API message index. A display message may be a placeholder (index -1) or out of range, meaning its API counterpart was removed by drop-oldest compaction — restarting from it would restart from an empty history — so this error is thrown.

Source

Thrown at frontend/src/lib/components/copilot/chat/AIChatManager.svelte.ts:4039

					images ?? [],
					files ?? []
				)
			}
			// "Text", not "message": chip edits are the part that does not survive.
			sendUserToast('This session is running in another tab. Your text was kept.', true)
			return
		}

		// Resolve the API restart point BEFORE reserving bytes or truncating: a
		// stale index must fail while nothing has been mutated, or the transcript
		// would be left truncated with the reservation leaked. A negative index
		// marks a message whose API counterpart was removed by drop-oldest
		// compaction — everything before it went too, so restarting from it
		// restarts from an empty history.
		const actualMessageIndex =
			userMessage.index < 0 ? 0 : userMessage.index < this.messages.length ? userMessage.index : -1
		if (actualMessageIndex === -1) {
			throw new Error('No actual user message found to restart from')
		}

		// Read while both arrays are intact: storedImages pairs the API message with
		// its transcript entry, and the truncations below drop them.
		const sentImages = this.storedImages(displayMessageIndex)

		// Reserve the resent files' bytes across the gap between the edit box
		// unmounting and the optimistic message landing. A per-resend token owns the
		// reservation (sendRequest releases only this key) so an unrelated or
		// concurrent send never clears it. Set before the slice below removes the
		// message from the transcript, so those bytes are always accounted.
		const resendReservationKey = `resend:${createLongHash()}`
		const resentFiles = files ?? userMessage.files ?? []
		this.setComposerStaged(
			resendReservationKey,
			null,
			resentFiles.reduce((sum, f) => sum + textByteLength(f.content), 0)
		)

View on GitHub (pinned to e474e8803c)

Solutions

  1. Restart from a more recent message whose API entry still exists.
  2. Refresh the session/transcript from the server to get post-compaction state before restarting.
  3. Start a new session instead of restarting from a compacted message.
  4. Disable/raise the drop-oldest compaction threshold if restarts from old messages are required.

Example fix

// before
await manager.restartFrom(oldDisplayIndex) // may hit compacted message
// after
const msg = manager.displayMessages[oldDisplayIndex]
if (msg && msg.index >= 0 && msg.index < manager.messages.length) {
  await manager.restartFrom(oldDisplayIndex)
} else {
  toast('This message can no longer be restarted from — start a new session')
}
Defensive patterns

Strategy: validation

Validate before calling

const msg = manager.displayMessages[i]
if (!msg || msg.index < 0 || msg.index >= manager.messages.length) {
  throw new Error('Message no longer restartable (compacted)')
}

Try / catch

try {
  await manager.restartFrom(i)
} catch (e) {
  if (e.message.includes('restart from')) {
    toast('This message was compacted; start a new session instead')
    return
  }
  throw e
}

Prevention

When it happens

Trigger: Calling the restart method on a user message whose stored index is -1 (compaction placeholder) or >= messages.length — i.e. the conversation was compacted or truncated after the user captured the message reference.

Common situations: Long conversations where drop-oldest compaction removed the oldest API messages while the UI still shows them; attempting to restart from a very old message in a big session; stale UI state after background compaction.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/8898c57506edf9f3. Report an issue: GitHub.