plandex-ai/plandex · error

no convo message ID for description: %v

Error message

no convo message ID for description: %v

What it means

Thrown when a plan description has pending build operations or invalidated build paths but its ConvoMessageId field is empty. The code requires every pending description to reference the conversation message that produced it, so it aborts instead of building an ActiveBuild with no ReplyId.

Source

Thrown at app/server/types/active_plan_pending_builds.go:44

		if err != nil {
			return nil, fmt.Errorf("error getting plan convo: %v", err)
		}
	} else {
		convoMessages = convoMessagesArg
	}

	convoMessagesById := map[string]*db.ConvoMessage{}
	for _, msg := range convoMessages {
		convoMessagesById[msg.Id] = msg
	}

	activeBuildsByPath := map[string][]*ActiveBuild{}

	for _, desc := range planDescs {
		if (!desc.DidBuild && len(desc.Operations) > 0) || len(desc.BuildPathsInvalidated) > 0 {
			if desc.ConvoMessageId == "" {
				log.Printf("No convo message ID for description: %v\n", desc)
				return nil, fmt.Errorf("no convo message ID for description: %v", desc)
			}

			if convoMessagesById[desc.ConvoMessageId] == nil {
				log.Printf("No convo message for ID: %s\n", desc.ConvoMessageId)
				return nil, fmt.Errorf("no convo message for ID: %s", desc.ConvoMessageId)
			}

			// convoMessage := convoMessagesById[desc.ConvoMessageId]

			// replyParser := NewReplyParser()
			// replyParser.AddChunk(convoMessage.Message, false)
			// parserRes := replyParser.FinishAndRead()

			numAdded := 0
			for _, op := range desc.Operations {

				if desc.DidBuild && !desc.BuildPathsInvalidated[op.Path] {
					continue

View on GitHub (pinned to e2d772072e)

Solutions

  1. Inspect the offending description (printed in the log line above the error) and fix its convo_message_id in the database.
  2. Rebuild or regenerate the plan's conversation descriptions so ConvoMessageId is populated.
  3. Skip or delete the orphaned description record if its builds are no longer needed.
  4. Add a DB constraint/validation on write so descriptions with operations always carry a ConvoMessageId.

Example fix

// before
if desc.ConvoMessageId == "" {
    return nil, fmt.Errorf("no convo message ID for description: %v", desc)
}
// after (skip-and-warn instead of hard fail)
if desc.ConvoMessageId == "" {
    log.Printf("skipping description %s with no convo message ID", desc.Id)
    continue
}
Defensive patterns

Strategy: validation

Validate before calling

for _, desc := range descs {
    if (!desc.DidBuild && len(desc.Operations) > 0) || len(desc.BuildPathsInvalidated) > 0 {
        if desc.ConvoMessageId == "" {
            return fmt.Errorf("description %s has pending builds but no convo message ID", desc.Id)
        }
    }
}

Type guard

func hasConvoMessageId(desc *types.ConvoMessageDescription) bool {
    return desc != nil && desc.ConvoMessageId != ""
}

Try / catch

builds, err := plan.PendingBuildsByPath(orgId, userId, nil)
if err != nil {
    if strings.Contains(err.Error(), "no convo message ID for description") {
        log.Printf("orphaned description detected; repairing descriptions before retry")
        return repairDescriptions(plan.Id)
    }
    return err
}

Prevention

When it happens

Trigger: A conversation message description row exists in the DB with empty convo_message_id while having len(Operations) > 0 with !DidBuild, or non-empty BuildPathsInvalidated; typically a legacy or corrupted description record.

Common situations: Older plan data created before ConvoMessageId was always populated; manually edited or imported DB rows; a bug in the description-creation path that skipped setting ConvoMessageId.

Related errors


AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05). Data as JSON: /api/errors/e8f1b3a36bfd5858. Report an issue: GitHub.