{"record":{"id":"e8f1b3a36bfd5858","repo":"plandex-ai/plandex","slug":"no-convo-message-id-for-description-v","errorCode":null,"errorMessage":"no convo message ID for description: %v","messagePattern":"no convo message ID for description: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/server/types/active_plan_pending_builds.go","lineNumber":44,"sourceCode":"\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error getting plan convo: %v\", err)\n\t\t}\n\t} else {\n\t\tconvoMessages = convoMessagesArg\n\t}\n\n\tconvoMessagesById := map[string]*db.ConvoMessage{}\n\tfor _, msg := range convoMessages {\n\t\tconvoMessagesById[msg.Id] = msg\n\t}\n\n\tactiveBuildsByPath := map[string][]*ActiveBuild{}\n\n\tfor _, desc := range planDescs {\n\t\tif (!desc.DidBuild && len(desc.Operations) > 0) || len(desc.BuildPathsInvalidated) > 0 {\n\t\t\tif desc.ConvoMessageId == \"\" {\n\t\t\t\tlog.Printf(\"No convo message ID for description: %v\\n\", desc)\n\t\t\t\treturn nil, fmt.Errorf(\"no convo message ID for description: %v\", desc)\n\t\t\t}\n\n\t\t\tif convoMessagesById[desc.ConvoMessageId] == nil {\n\t\t\t\tlog.Printf(\"No convo message for ID: %s\\n\", desc.ConvoMessageId)\n\t\t\t\treturn nil, fmt.Errorf(\"no convo message for ID: %s\", desc.ConvoMessageId)\n\t\t\t}\n\n\t\t\t// convoMessage := convoMessagesById[desc.ConvoMessageId]\n\n\t\t\t// replyParser := NewReplyParser()\n\t\t\t// replyParser.AddChunk(convoMessage.Message, false)\n\t\t\t// parserRes := replyParser.FinishAndRead()\n\n\t\t\tnumAdded := 0\n\t\t\tfor _, op := range desc.Operations {\n\n\t\t\t\tif desc.DidBuild && !desc.BuildPathsInvalidated[op.Path] {\n\t\t\t\t\tcontinue","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/types/active_plan_pending_builds.go#L26-L62","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect the offending description (printed in the log line above the error) and fix its convo_message_id in the database.","Rebuild or regenerate the plan's conversation descriptions so ConvoMessageId is populated.","Skip or delete the orphaned description record if its builds are no longer needed.","Add a DB constraint/validation on write so descriptions with operations always carry a ConvoMessageId."],"exampleFix":"// before\nif desc.ConvoMessageId == \"\" {\n    return nil, fmt.Errorf(\"no convo message ID for description: %v\", desc)\n}\n// after (skip-and-warn instead of hard fail)\nif desc.ConvoMessageId == \"\" {\n    log.Printf(\"skipping description %s with no convo message ID\", desc.Id)\n    continue\n}","handlingStrategy":"validation","validationCode":"for _, desc := range descs {\n    if (!desc.DidBuild && len(desc.Operations) > 0) || len(desc.BuildPathsInvalidated) > 0 {\n        if desc.ConvoMessageId == \"\" {\n            return fmt.Errorf(\"description %s has pending builds but no convo message ID\", desc.Id)\n        }\n    }\n}","typeGuard":"func hasConvoMessageId(desc *types.ConvoMessageDescription) bool {\n    return desc != nil && desc.ConvoMessageId != \"\"\n}","tryCatchPattern":"builds, err := plan.PendingBuildsByPath(orgId, userId, nil)\nif err != nil {\n    if strings.Contains(err.Error(), \"no convo message ID for description\") {\n        log.Printf(\"orphaned description detected; repairing descriptions before retry\")\n        return repairDescriptions(plan.Id)\n    }\n    return err\n}","preventionTips":["Always set ConvoMessageId when creating ConvoMessageDescription rows.","Add a NOT NULL / CHECK constraint on convo_message_id for descriptions with operations.","Backfill legacy rows missing the ID before running builds.","Log and quarantine orphaned descriptions rather than leaving them in the active set."],"tags":["data-integrity","go","missing-id"],"backgroundTag":"foreign-key-missing","analyzedSha":"e2d772072efadbe41d2946d97d79be55532dbab5","analyzedAt":"2026-09-05T20:56:53.631Z","contentChangedAt":"2026-09-05T20:56:53.631Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}