{"record":{"id":"fad8ac8a47b318f5","repo":"gastownhall/beads","slug":"comment-created-at-is-zero-for-issue-s","errorCode":null,"errorMessage":"comment created_at is zero for issue %s","messagePattern":"comment created_at is zero for issue (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/migration/legacysqlite/reader.go","lineNumber":975,"sourceCode":"\t\tif err := checkCurrentVarchars(\n\t\t\tcurrentVarchar{\"comment issue_id\", issueID, types.MaxFieldLen},\n\t\t\tcurrentVarchar{\"comment author\", author, types.MaxFieldLen},\n\t\t); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tissue := byID[issueID]\n\t\tif issue == nil {\n\t\t\treturn fmt.Errorf(\"orphan comment for %s\", issueID)\n\t\t}\n\t\tif issue.Ephemeral && len(text) > currentTextBytes {\n\t\t\treturn fmt.Errorf(\"legacy SQLite ephemeral comment text is %d bytes (current TEXT maximum %d)\", len(text), currentTextBytes)\n\t\t}\n\t\tcreated, e := parseTime(at)\n\t\tif e != nil {\n\t\t\treturn e\n\t\t}\n\t\tif created.IsZero() {\n\t\t\treturn fmt.Errorf(\"comment created_at is zero for issue %s\", issueID)\n\t\t}\n\t\tidentity := commentIdentity{issueID: issueID, author: author, text: text, createdAt: created}\n\t\tif priorID, exists := seenComments[identity]; exists {\n\t\t\treturn fmt.Errorf(\"legacy SQLite comments %d and %d share current import identity\", priorID, id)\n\t\t}\n\t\tseenComments[identity] = id\n\t\tissue.Comments = append(issue.Comments, &types.Comment{ID: strconv.FormatInt(id, 10), IssueID: issueID, Author: author, Text: text, CreatedAt: created})\n\t}\n\treturn comments.Err()\n}\n\nfunc validateDependencyGraph(issues []*types.Issue) error {\n\tscheduling := make(map[string][]string)\n\thierarchy := make(map[string][]string)\n\tvar blocking []*types.Dependency\n\tfor _, issue := range issues {\n\t\tfor _, dep := range issue.Dependencies {\n\t\t\tif dep.IssueID == dep.DependsOnID {","sourceCodeStart":957,"sourceCodeEnd":993,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/migration/legacysqlite/reader.go#L957-L993","documentation":"Every comment must carry a real creation timestamp; the reader parses created_at and rejects comments whose parsed time is the zero time.Time. bd uses comment CreatedAt for ordering and identity, so zero timestamps cannot be imported.","triggerScenarios":"Legacy SQLite comment rows with NULL/empty created_at, or timestamps in a format `parseTime` cannot interpret and which resolve to zero; reported per issue ID.","commonSituations":"Very old schema versions before created_at existed on comments; rows inserted by scripts that omitted the column; clock/corruption producing unparseable date strings.","solutions":["Backfill created_at in the legacy DB: UPDATE comments SET created_at = <iso timestamp> WHERE created_at IS NULL OR created_at = ''","Normalize unparseable timestamps to the expected format (RFC3339) before migrating","If the true time is unrecoverable, use the issue's created_at as a fallback for the affected comments"],"exampleFix":"-- before\n-- comment created_at NULL\nUPDATE comments SET created_at = (SELECT created_at FROM issues WHERE id = comments.issue_id)\nWHERE created_at IS NULL OR created_at = '';\n-- after: comment inherits its issue's timestamp","handlingStrategy":"validation","validationCode":"for (const c of legacyComments) {\n  if (!c.created_at || isNaN(Date.parse(c.created_at)))\n    throw new Error(`zero/invalid comment created_at on issue ${c.issue_id}`);\n}","typeGuard":"function commentHasCreatedAt(c) {\n  return typeof c.created_at === 'string' && c.created_at !== '' && !isNaN(Date.parse(c.created_at));\n}","tryCatchPattern":"try { migrateLegacySQLite(dbPath) } catch (e) {\n  if (e.message.includes('comment created_at is zero')) {\n    const issueID = e.message.match(/issue (\\S+)/)[1];\n    backfillCommentCreatedAt(dbPath, issueID); // fall back to issue created_at\n    retry();\n  } else throw e;\n}","preventionTips":["Backfill NULL created_at on comments before import (issue timestamp is a good fallback)","Enforce NOT NULL on comments.created_at","Normalize timestamp formats to RFC3339 pre-migration"],"tags":["migration","sqlite","timestamp"],"backgroundTag":"missing-created-at","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}