{"record":{"id":"9f0ddd8a65292254","repo":"JuliusBrussee/caveman","slug":"cachebench-invalid-observation-read-limits","errorCode":null,"errorMessage":"cachebench: invalid observation read limits","messagePattern":"cachebench: invalid observation read limits","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cacheengine/cachebench/observed.go","lineNumber":97,"sourceCode":"type ObservationReadLimits struct {\n\tMaxLineBytes int\n\tMaxRecords   int\n}\n\n// DefaultObservationReadLimits returns conservative retained-evidence limits.\nfunc DefaultObservationReadLimits() ObservationReadLimits {\n\treturn ObservationReadLimits{MaxLineBytes: 8 << 20, MaxRecords: 100_000}\n}\n\n// ReadObservationJSONL reads observation records under default limits.\nfunc ReadObservationJSONL(reader io.Reader) ([]ObservationRecord, error) {\n\treturn ReadObservationJSONLWithLimits(reader, DefaultObservationReadLimits())\n}\n\n// ReadObservationJSONLWithLimits reads strict observation JSONL under explicit limits.\nfunc ReadObservationJSONLWithLimits(reader io.Reader, limits ObservationReadLimits) ([]ObservationRecord, error) {\n\tif limits.MaxLineBytes <= 0 || limits.MaxLineBytes > 64<<20 || limits.MaxRecords <= 0 || limits.MaxRecords > 1_000_000 {\n\t\treturn nil, errors.New(\"cachebench: invalid observation read limits\")\n\t}\n\tscanner := bufio.NewScanner(reader)\n\tinitial := 64 * 1024\n\tif limits.MaxLineBytes < initial {\n\t\tinitial = limits.MaxLineBytes\n\t}\n\tscanner.Buffer(make([]byte, initial), limits.MaxLineBytes)\n\tseen := map[string]bool{}\n\tvar records []ObservationRecord\n\tfor line := 1; scanner.Scan(); line++ {\n\t\traw := bytes.TrimSpace(scanner.Bytes())\n\t\tif len(raw) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tif len(records) >= limits.MaxRecords {\n\t\t\treturn nil, fmt.Errorf(\"cachebench: observations exceed record limit %d\", limits.MaxRecords)\n\t\t}\n\t\tif !validUniqueJSONObject(raw) {","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/cacheengine/cachebench/observed.go#L79-L115","documentation":"ReadObservationJSONLWithLimits requires MaxLineBytes in (0, 64MiB] and MaxRecords in (0, 1,000,000]. Zero, negative, or over-cap values are rejected because the scanner buffer and memory bounds depend on them being sane; the defaults are 8MiB lines and 100k records.","triggerScenarios":"Passing a custom ObservationReadLimits with MaxLineBytes <= 0 or > 64<<20, or MaxRecords <= 0 or > 1_000_000 — e.g. copying a config where the fields defaulted to zero because only one was set.","commonSituations":"Partial struct literal ObservationReadLimits{MaxLineBytes: 1<<20} leaving MaxRecords zero; config-driven limits where a missing key decodes as 0; attempting to disable limits by setting 0.","solutions":["Set both fields explicitly within the allowed ranges (or call ReadObservationJSONL to use defaults)","Derive custom limits from DefaultObservationReadLimits() and adjust one field","Treat 0-values in your config as 'use defaults' and substitute before calling"],"exampleFix":"// before\nlimits := ObservationReadLimits{MaxLineBytes: 1 << 20} // MaxRecords == 0\nrecs, err := ReadObservationJSONLWithLimits(r, limits)\n\n// after\nlimits := DefaultObservationReadLimits()\nlimits.MaxLineBytes = 1 << 20\nrecs, err := ReadObservationJSONLWithLimits(r, limits)","handlingStrategy":"validation","validationCode":"func validReadLimits(l ObservationReadLimits) bool {\n    return l.MaxLineBytes > 0 && l.MaxLineBytes <= 64<<20 && l.MaxRecords > 0 && l.MaxRecords <= 1_000_000\n}\n\nlimits := DefaultObservationReadLimits()\nif !validReadLimits(limits) { return errors.New(\"bad limits\") }","typeGuard":"func withinObservationLimits(l ObservationReadLimits) bool {\n    return l.MaxLineBytes > 0 && l.MaxLineBytes <= 64<<20 && l.MaxRecords > 0 && l.MaxRecords <= 1_000_000\n}","tryCatchPattern":"if _, err := cachebench.ReadObservationJSONLWithLimits(r, limits); err != nil {\n    if err.Error() == \"cachebench: invalid observation read limits\" {\n        limits = cachebench.DefaultObservationReadLimits() // reset and retry once\n    }\n}","preventionTips":["Start from DefaultObservationReadLimits() and override one field at a time","Treat zero-valued limit fields in config as 'missing' and substitute defaults","Remember 0 does not mean unlimited here — it is invalid"],"tags":["go","observations","configuration","limits"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}