chenhg5/cc-connect · error
max: update message: no message id in reply context
Error message
max: update message: no message id in reply context
What it means
After the type assertion succeeds, UpdateMessage checks that the replyContext carries a non-empty messageID, because editing is done via PUT /messages?message_id=<id>. This error means the reply context is a valid MAX context but was created without a message id — typically a context reconstructed from a session key ("max:{chatID}") where only the chatID is restored, so the platform doesn't know which message to edit.
Source
Thrown at platform/max/max.go:500
return fmt.Errorf("max: upload audio: %w", err)
}
body := &maxSendBody{
Attachments: []maxOutAttachment{{
Type: "audio",
Payload: maxTokenPayload{Token: token},
}},
}
return p.postMessage(ctx, rctx.chatID, body)
}
// UpdateMessage implements core.MessageUpdater via PUT /messages?message_id=.
func (p *Platform) UpdateMessage(ctx context.Context, replyCtx any, content string) error {
rctx, ok := replyCtx.(replyContext)
if !ok {
return fmt.Errorf("max: unexpected replyCtx type %T", replyCtx)
}
if rctx.messageID == "" {
return fmt.Errorf("max: update message: no message id in reply context")
}
body := maxSendBody{Text: content, Format: "markdown"}
data, err := json.Marshal(body)
if err != nil {
return err
}
req, err := http.NewRequestWithContext(ctx, http.MethodPut, p.apiBase+"/messages", bytes.NewReader(data))
if err != nil {
return err
}
p.setAuth(req)
q := req.URL.Query()
q.Set("message_id", rctx.messageID)
req.URL.RawQuery = q.Encode()
req.Header.Set("Content-Type", "application/json")
resp, err := p.client.Do(req)
if err != nil {View on GitHub (pinned to 4000b2338a)
Solutions
- Edit using a reply context captured from an actual MAX inbound message (handleMessage sets messageID from msg.Body.Mid) rather than a reconstructed one.
- Persist the messageID alongside the session if you need to edit after a restart; reconstruct it yourself only if the API allows editing by a stored id.
- Check whether the message you are trying to edit is one your bot sent in reply — MAX's edit API needs that message's id; obtain it from the send response instead of the inbound context.
- Fall back to sending a new message (Reply) when no messageID is available, instead of editing.
Example fix
// before
rctx, _ := platform.ReconstructReplyCtx("max:" + chatID) // messageID is empty
err := platform.UpdateMessage(ctx, rctx, newText) // fails: no message id
// after
if rctx, ok := liveReplyCtx.(replyContext); ok && rctx.messageID != "" {
err = platform.UpdateMessage(ctx, liveReplyCtx, newText)
} else {
err = platform.Reply(ctx, liveReplyCtx, newText) // fallback: send new message
} Defensive patterns
Strategy: validation
Validate before calling
// Called before UpdateMessage; messageID is unexported, so gate on origin:
func canEdit(replyCtx any) bool {
rctx, ok := replyCtx.(replyContext)
return ok && rctx.messageID != ""
} Type guard
rctx, ok := replyCtx.(replyContext)
if !ok || rctx.messageID == "" {
// cannot edit: reconstructed or id-less context — use Reply instead
} Try / catch
if err := platform.UpdateMessage(ctx, replyCtx, content); err != nil {
if strings.Contains(err.Error(), "no message id in reply context") {
return platform.Reply(ctx, replyCtx, content) // graceful degradation: new message
}
return err
} Prevention
- Use reply contexts captured from live inbound messages for edits, not ReconstructReplyCtx output (which has an empty messageID).
- Persist the bot's own sent-message id if you need to edit after a daemon restart.
- Design edit flows to degrade gracefully to sending a fresh message when no id is known.
- Log the session-key provenance of a reply context so you can tell reconstructed (chatID-only) from live ones.
When it happens
Trigger: Calling UpdateMessage with a replyContext produced by ReconstructReplyCtx (which sets chatID but leaves messageID empty); a replyContext value-built by custom code without messageID; an inbound event whose Body.Mid was empty so handleMessage stored an empty id.
Common situations: Resuming a session after a daemon restart: the reply context is rebuilt from the persisted session key, losing the message id, and a later edit (e.g. streaming update or /history rewrite) targets it; custom integrations that persist and restore reply contexts without the message id field.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- max: edit message: %w
- bridge: invalid reply context type %T
- max: upload audio: %w
- wecom-ws: SendImage: invalid reply context type %T
- acp: no agent session id
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/6c3ad570b5bb76c8.
Report an issue: GitHub.