{"record":{"id":"691cd9d9eeb236ae","repo":"JuliusBrussee/caveman","slug":"configure-sqlite-storage-budget-q-w","errorCode":null,"errorMessage":"configure sqlite storage budget %q: %w","messagePattern":"configure sqlite storage budget %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"engine/ccr/store_sqlite.go","lineNumber":229,"sourceCode":"\t\t\treturn nil, fmt.Errorf(\"read sqlite journal mode: %w\", err)\n\t\t}\n\t\twalJournal = strings.EqualFold(mode, \"wal\")\n\t}\n\tif err := RetryOnBusy(func() error { _, e := db.Exec(schema); return e }); err != nil {\n\t\tcloseSQLiteAfterOpenFailure(db, canonicalPath)\n\t\treturn nil, fmt.Errorf(\"migrate sqlite %q: %w\", canonicalPath, err)\n\t}\n\tif err := RetryOnBusy(func() error { return ensureMetadataColumn(db) }); err != nil {\n\t\tcloseSQLiteAfterOpenFailure(db, canonicalPath)\n\t\treturn nil, fmt.Errorf(\"migrate sqlite metadata %q: %w\", canonicalPath, err)\n\t}\n\tif err := RetryOnBusy(func() error { return ensureDataRefColumn(db) }); err != nil {\n\t\t_ = db.Close()\n\t\treturn nil, fmt.Errorf(\"migrate sqlite data_ref %q: %w\", canonicalPath, err)\n\t}\n\tif err := configureStorageBudget(db, maxBytes); err != nil {\n\t\tcloseSQLiteAfterOpenFailure(db, canonicalPath)\n\t\treturn nil, fmt.Errorf(\"configure sqlite storage budget %q: %w\", canonicalPath, err)\n\t}\n\topened, err := inspectSQLiteGeneration(canonicalPath)\n\tif err != nil {\n\t\tcloseSQLiteAfterOpenFailure(db, canonicalPath)\n\t\treturn nil, err\n\t}\n\tif !before.sameExisting(opened) || (walJournal && (opened[1] == nil || opened[2] == nil)) {\n\t\tcloseSQLiteAfterOpenFailure(db, canonicalPath)\n\t\treturn nil, ErrStorageChanged\n\t}\n\t// The schema/budget queries have opened the WAL. Capture the resulting\n\t// journals before final permission checks, so losing a newly-created\n\t// journal is distinguishable from normal creation during SQLite open.\n\tbefore = opened\n\tif afterOpen != nil {\n\t\tafterOpen()\n\t}\n\tif err := secureSQLiteFiles(canonicalPath); err != nil {","sourceCodeStart":211,"sourceCodeEnd":247,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/engine/ccr/store_sqlite.go#L211-L247","documentation":"This error wraps failure of configureStorageBudget, which applies the configured max storage size to the SQLite store (e.g. PRAGMA max_page_count or equivalent) during open. If the budget cannot be set, the store closes the db and aborts the open because an unbounded database would violate its retention guarantees.","triggerScenarios":"configureStorageBudget's pragma/query fails due to lock contention, or the requested maxBytes value translates to an invalid/unsupported pragma value (e.g. budget below current file size or non-positive).","commonSituations":"Passing a maxBytes value smaller than the existing database; another process holding a lock; db on read-only media.","solutions":["Verify maxBytes is positive and larger than the current database size; increase it or delete/compact the db","Ensure no other process holds a write lock during open","Check the db file is writable (setting the budget requires a write)","Unwrap the %w chain to read the underlying SQLite error for the exact cause"],"exampleFix":"// before\nstore, err := ccr.Open(dbPath, ccr.WithMaxBytes(1024)) // smaller than existing 5 MB db\n// after\ninfo, _ := os.Stat(dbPath)\nmaxBytes := int64(1024)\nif info != nil && info.Size() > maxBytes {\n    maxBytes = info.Size() * 2 // budget above current size\n}\nstore, err := ccr.Open(dbPath, ccr.WithMaxBytes(maxBytes))","handlingStrategy":"validation","validationCode":"func validateBudget(dbPath string, maxBytes int64) error {\n    if maxBytes <= 0 {\n        return fmt.Errorf(\"maxBytes must be positive\")\n    }\n    if fi, err := os.Stat(dbPath); err == nil && fi.Size() >= maxBytes {\n        return fmt.Errorf(\"maxBytes %d below current db size %d\", maxBytes, fi.Size())\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always configure a maxBytes above the current database size","Validate budget values at config load time","Monitor db size so the budget stays ahead of growth"],"tags":["sqlite","storage-budget","configuration"],"backgroundTag":"invalid-config-value","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}