{"record":{"id":"e04201c6055e4842","repo":"gastownhall/beads","slug":"invalid-storage-class-q","errorCode":null,"errorMessage":"invalid storage class %q","messagePattern":"invalid storage class %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/types/types.go","lineNumber":965,"sourceCode":"func (m PersistenceMode) IsValid() bool {\n\treturn slices.Contains(validPersistenceModes, m)\n}\n\n// NormalizePersistenceMode maps a requested mode from current to its exact\n// persistence fields without mutating an issue. The returned values are\n// Ephemeral, NoHistory, and StorageClass, respectively. Plane and retention\n// 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","sourceCodeStart":947,"sourceCodeEnd":983,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/types/types.go#L947-L983","documentation":"NormalizePersistenceMode validates the issue's current StorageClass against the canonical valid list before applying a mode change. An unknown or corrupted class value aborts the operation to avoid writing inconsistent storage metadata.","triggerScenarios":"Calling NormalizePersistenceMode when current.StorageClass holds a value not in validStorageClasses (fails StorageClass.IsValid()) — e.g. \"\" when a class was required, a typo like \"versioed\", or data written by an incompatible version.","commonSituations":"Hand-edited or externally migrated JSONL records; database rows from an older schema using a retired class name; code assigning raw strings to StorageClass instead of constants.","solutions":["Set StorageClass to one of the valid constants (versioned/ephemeral/unversioned or empty where allowed)","Parse user input through ParseStorageClass instead of direct casting","Repair or re-export the record if its stored class is corrupt/legacy"],"exampleFix":"// before\nissue.StorageClass = StorageClass(\"versioed\")\n// after\nissue.StorageClass = StorageClassVersioned","handlingStrategy":"validation","validationCode":"if issue.StorageClass != \"\" && !issue.StorageClass.IsValid() {\n    return fmt.Errorf(\"issue %s has invalid storage class %q\", issue.ID, issue.StorageClass)\n}","typeGuard":"func hasValidStorageClass(i Issue) bool {\n    return i.StorageClass == \"\" || i.StorageClass.IsValid()\n}","tryCatchPattern":"res, err := NormalizePersistenceMode(issue, mode)\nif err != nil && strings.Contains(err.Error(), \"invalid storage class\") {\n    // repair or reset StorageClass before retrying\n}","preventionTips":["Assign StorageClass only via constants or ParseStorageClass","Validate exported/imported records before writing them back","Check for retired class names when upgrading between versions"],"tags":["storage-class","persistence","validation"],"backgroundTag":"invalid-storage-class","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}