{"record":{"id":"d71dbd79d06019d6","repo":"gastownhall/beads","slug":"storage-class-ephemeral-requires-ephemeral-or-no-h","errorCode":null,"errorMessage":"storage class ephemeral requires ephemeral or no_history","messagePattern":"storage class ephemeral requires ephemeral or no_history","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/types/types.go","lineNumber":972,"sourceCode":"// changes preserve the create-selected class. Promotion clears an explicit\n// ephemeral class marker to select normalized versioned storage. An\n// unversioned record may remain persistent but cannot move to a wisp mode.\nfunc NormalizePersistenceMode(current Issue, mode PersistenceMode) (bool, bool, StorageClass, error) {\n\tif !mode.IsValid() {\n\t\treturn false, false, \"\", fmt.Errorf(\"invalid persistence mode %q\", mode)\n\t}\n\tif current.Ephemeral && current.NoHistory {\n\t\treturn false, false, \"\", fmt.Errorf(\"ephemeral and no_history are mutually exclusive\")\n\t}\n\tif !current.StorageClass.IsValid() {\n\t\treturn false, false, \"\", fmt.Errorf(\"invalid storage class %q\", current.StorageClass)\n\t}\n\twispPlane := current.Ephemeral || current.NoHistory\n\tif wispPlane && current.StorageClass != \"\" && current.StorageClass != StorageClassEphemeral {\n\t\treturn false, false, \"\", fmt.Errorf(\"storage class %q conflicts with ephemeral/no_history\", current.StorageClass)\n\t}\n\tif !wispPlane && current.StorageClass == StorageClassEphemeral {\n\t\treturn false, false, \"\", fmt.Errorf(\"storage class ephemeral requires ephemeral or no_history\")\n\t}\n\tif current.StorageClass == StorageClassUnversioned && mode != PersistenceModePersistent {\n\t\treturn false, false, \"\", fmt.Errorf(\"cannot move unversioned record to persistence mode %q\", mode)\n\t}\n\tif persistenceMode(current) == mode {\n\t\treturn current.Ephemeral, current.NoHistory, current.StorageClass, nil\n\t}\n\n\tswitch mode {\n\tcase PersistenceModePersistent:\n\t\tstorageClass := current.StorageClass\n\t\tif storageClass == StorageClassEphemeral {\n\t\t\tstorageClass = \"\"\n\t\t}\n\t\treturn false, false, storageClass, nil\n\tcase PersistenceModeEphemeral:\n\t\tstorageClass := current.StorageClass\n\t\tif storageClass == StorageClassVersioned {","sourceCodeStart":954,"sourceCodeEnd":990,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/types/types.go#L954-L990","documentation":"The ephemeral storage class only makes sense for records on the wisp plane. A persistent, versioned record (Ephemeral and NoHistory false) cannot claim storage class 'ephemeral', so normalization rejects the state.","triggerScenarios":"Calling NormalizePersistenceMode with current.Ephemeral == false, current.NoHistory == false, and current.StorageClass == StorageClassEphemeral — e.g. a record demoted to ephemeral earlier and then flipped back to persistent without clearing the class.","commonSituations":"Promoting a wisp back to a persistent record while its ephemeral marker was left behind; manual edits to exported data; code paths that clear Ephemeral but not StorageClass.","solutions":["Clear StorageClass (letting it default to versioned) when promoting the record out of the wisp plane","Set StorageClass to a persistent-appropriate class (versioned/unversioned) along with clearing the wisp flags","Always apply plane changes through NormalizePersistenceMode rather than setting fields directly"],"exampleFix":"// before\nissue.Ephemeral = false; issue.StorageClass = StorageClassEphemeral\n// after\nissue.Ephemeral = false; issue.StorageClass = \"\"","handlingStrategy":"validation","validationCode":"if !issue.Ephemeral && !issue.NoHistory && issue.StorageClass == StorageClassEphemeral {\n    return fmt.Errorf(\"persistent issue %s has ephemeral storage class\", issue.ID)\n}","typeGuard":"func orphanedEphemeralClass(i Issue) bool {\n    return !i.Ephemeral && !i.NoHistory && i.StorageClass == StorageClassEphemeral\n}","tryCatchPattern":"res, err := NormalizePersistenceMode(issue, mode)\nif err != nil && strings.Contains(err.Error(), \"requires ephemeral or no_history\") {\n    issue.StorageClass = \"\"\n    res, err = NormalizePersistenceMode(issue, mode)\n}","preventionTips":["Clear the ephemeral class marker when promoting a wisp to persistent","Never clear wisp flags without also normalizing StorageClass","Promote records only via the library's promotion path, not direct field edits"],"tags":["storage-class","ephemeral","state-conflict"],"backgroundTag":"storage-class-plane-conflict","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}