{"record":{"id":"41fa6bea010c5e25","repo":"gastownhall/beads","slug":"import-memory-q-w","errorCode":null,"errorMessage":"import memory %q: %w","messagePattern":"import memory %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/uow/importer.go","lineNumber":95,"sourceCode":"\t\t\t\t\t})\n\t\t\t\t},\n\t\t\t\tOnStaleRejected: func(issueID string) {\n\t\t\t\t\tif _, ok := staleRejected[issueID]; ok {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tstaleRejected[issueID] = struct{}{}\n\t\t\t\t\tresult.StaleRejectedIDs = append(result.StaleRejectedIDs, issueID)\n\t\t\t\t},\n\t\t\t}\n\t\t\tif _, err := storageissueops.CreateIssuesInTxWithResult(ctx, runner, request.Issues, request.Actor, opts); err != nil {\n\t\t\t\treturn publicops.ImportBatchResult{}, \"\", err\n\t\t\t}\n\t\t\tresult.Created = len(request.Issues) - len(staleRejected)\n\t\t}\n\n\t\tfor _, memory := range request.Memories {\n\t\t\tif err := uw.ConfigUseCase().SetConfig(ctx, memory.Key, memory.Value); err != nil {\n\t\t\t\treturn publicops.ImportBatchResult{}, \"\", fmt.Errorf(\"import memory %q: %w\", memory.Key, err)\n\t\t\t}\n\t\t\tresult.MemoriesImported++\n\t\t}\n\n\t\t// config.yaml is authoritative for issue_prefix on the import flow\n\t\t// (be-llaf); a read or write failure here degrades to \"not synced\"\n\t\t// rather than failing the batch, matching the classic path.\n\t\tif request.SyncIssuePrefix != \"\" {\n\t\t\tstored, _ := uw.ConfigUseCase().GetConfig(ctx, \"issue_prefix\")\n\t\t\tif stored != request.SyncIssuePrefix {\n\t\t\t\tif err := uw.ConfigUseCase().SetConfig(ctx, \"issue_prefix\", request.SyncIssuePrefix); err == nil {\n\t\t\t\t\tresult.PrefixSynced = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn result, importBatchCommitMessage(request, result), nil\n\t})","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/uow/importer.go#L77-L113","documentation":"While landing the import batch inside the unit of work, each memory record is written via the config use case (SetConfig). If that write fails, the error is wrapped with the offending memory key as \"import memory %q\". The whole batch transaction then aborts and rolls back, so nothing from the batch is committed.","triggerScenarios":"Calling ImportBatch with request.Memories whose Key cannot be written by uw.ConfigUseCase().SetConfig — e.g. an empty or invalid key, a key colliding with protected config, storage-layer failures (connection dropped mid-tx), or a duplicate write conflict inside the transaction.","commonSituations":"Import files (JSONL) containing malformed memory entries with empty keys or control characters; concurrent `bd` processes writing the same config key during sync; database connectivity loss partway through a large import.","solutions":["Sanitize memory entries before import: skip or fix entries with empty/invalid keys.","Retry the import after confirming DB connectivity if the wrapped error is a connection/serialization failure (RunTxResult already retries serialization, but hard I/O errors are not retried).","Inspect the wrapped inner error for the root cause — it names the exact SetConfig failure.","Update the failing key's value or drop the record from the import source, then re-run; the batch is atomic so no partial state needs cleanup."],"exampleFix":"// before\nfor _, m := range rawMemories {\n\trequest.Memories = append(request.Memories, publicops.MemoryEntry{Key: m.Key, Value: m.Value}) // m.Key may be \"\"\n}\n// after\nfor _, m := range rawMemories {\n\tif m.Key == \"\" {\n\t\tlog.Printf(\"skipping memory with empty key\")\n\t\tcontinue\n\t}\n\trequest.Memories = append(request.Memories, publicops.MemoryEntry{Key: m.Key, Value: m.Value})\n}","handlingStrategy":"try-catch","validationCode":"for i, m := range request.Memories {\n\tif strings.TrimSpace(m.Key) == \"\" {\n\t\treturn fmt.Errorf(\"memory entry %d has empty key\", i)\n\t}\n}","typeGuard":"func validMemories(ms []publicops.MemoryEntry) bool {\n\tfor _, m := range ms {\n\t\tif m.Key == \"\" {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}","tryCatchPattern":"result, err := imp.ImportBatch(ctx, req)\nif err != nil {\n\tvar key string\n\tif n, serr := fmt.Sscanf(err.Error(), \"import memory %q:\", &key); serr == nil && n == 1 {\n\t\treturn fmt.Errorf(\"import aborted at memory %q (batch rolled back): %w\", key, err)\n\t}\n\treturn err\n}","preventionTips":["Sanitize/skip empty or malformed memory keys when building the request from import files.","Treat imports as all-or-nothing: no partial state persists, so re-running is safe after fixing data.","Verify DB connectivity before large imports to avoid mid-transaction SetConfig failures.","Coordinate concurrent bd sync processes so the same config keys aren't written simultaneously."],"tags":["import","config","transaction","storage"],"backgroundTag":"config-write-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}