bytedance/deer-flow · info · HTTPException
Edited message cannot be empty
Error message
Edited message cannot be empty
What it means
Raised in _prepare_edit_regenerate_payload (HTTP 409) when the replacement text for an edited user message strips to an empty string. An edit that produces no user content cannot be replayed, so it is rejected up front before any checkpoint access.
Source
Thrown at backend/app/gateway/routers/thread_runs.py:721
# the older automatically generated title (#4457).
regenerate_input["title"] = latest_title
return RegeneratePrepareResponse(
input=regenerate_input,
checkpoint=checkpoint,
metadata=metadata,
target_run_id=target_run_id,
)
async def _prepare_edit_regenerate_payload(
thread_id: str,
human_message_id: str,
replacement_text: str,
request: Request,
) -> EditRegeneratePrepareResponse:
normalized_text = replacement_text.strip()
if not normalized_text:
raise HTTPException(status_code=409, detail="Edited message cannot be empty")
accessor, latest_config = await build_thread_checkpoint_state_accessor(request, thread_id=thread_id)
try:
latest_checkpoint = await accessor.aget(latest_config)
except Exception as exc:
logger.exception("Failed to read latest checkpoint for edit replay thread %s", thread_id)
raise HTTPException(status_code=500, detail="Failed to read latest checkpoint") from exc
latest_checkpoint_id = _checkpoint_configurable(latest_checkpoint).get("checkpoint_id")
if not latest_checkpoint_id:
raise HTTPException(status_code=404, detail=f"Thread {thread_id} has no checkpoint")
messages = _checkpoint_messages(latest_checkpoint)
if _has_active_goal(latest_checkpoint):
raise HTTPException(status_code=409, detail="Cannot edit while a goal is active")
_, source_human, _, source_ai, source_message_ids = _latest_editable_turn(messages, human_message_id)
source_text = get_original_user_content_text(_message_content(source_human), _message_additional_kwargs(source_human)).strip()
if normalized_text == source_text:View on GitHub (pinned to 1dd6ba1acb)
Solutions
- Client-side validate the edit box is non-empty (after trim) before enabling submit.
- Check the request field name matches the endpoint's schema exactly.
- Send the trimmed text explicitly in the request body.
Example fix
// before await editPrepare(threadId, humanId, draftText); // after const text = draftText.trim(); if (!text) return; await editPrepare(threadId, humanId, text);
Defensive patterns
Strategy: validation
Validate before calling
const text = (draft ?? '').trim();
if (!text) { keepEditorOpen(); return; } // do not call the endpoint Type guard
function isValidEditText(text: unknown): text is string {
return typeof text === 'string' && text.trim().length > 0;
} Try / catch
try { await editPrepare(threadId, humanId, text); } catch (e) { if (e.status === 409 && /cannot be empty/.test(e.detail)) { showFieldError('Message cannot be empty'); } else throw e; } Prevention
- Trim and require non-empty before submit
- Disable submit button for empty drafts
- Match the endpoint's field name exactly
When it happens
Trigger: POST to the edit-prepare endpoint with a body whose replacement/new_message field is '', whitespace-only, or missing.
Common situations: Frontend sends the form before the user types; whitespace-only paste; JSON field name mismatch (e.g. 'text' vs 'new_message') silently defaulting to empty.
Related errors
- Only the latest completed user turn can be edited
- Only completed assistant text turns can be edited
- Failed to load agents: ${res.statusText}
- Agent '${name}' not found
- backend_unreachable
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/c4e2482d9679fb8d.
Report an issue: GitHub.