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

  1. Edit using a reply context captured from an actual MAX inbound message (handleMessage sets messageID from msg.Body.Mid) rather than a reconstructed one.
  2. 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.
  3. 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.
  4. 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

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


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/6c3ad570b5bb76c8. Report an issue: GitHub.