{"record":{"id":"8e840a339c6aa5ec","repo":"JuliusBrussee/caveman","slug":"cachebench-no-trace-records","errorCode":null,"errorMessage":"cachebench: no trace records","messagePattern":"cachebench: no trace records","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cacheengine/cachebench/trace.go","lineNumber":342,"sourceCode":"\t\tif record.Schema == TraceSchema || record.Schema == TraceSchemaV2 {\n\t\t\tif record.ExpectedRPM <= 0 || record.ExpectedCalls <= 0 || !validTimingBasis(record.TimingBasis) {\n\t\t\t\treturn nil, fmt.Errorf(\"cachebench: trace line %d: incomplete replay metadata\", line)\n\t\t\t}\n\t\t}\n\t\tif record.Schema == TraceSchema {\n\t\t\tif record.DeclaredInputTokens <= 0 || record.DeclaredInputTokens < record.PrefixTokens || record.MaxOutputTokens <= 0 || !requestBudgetMatchesBody(record) {\n\t\t\t\treturn nil, fmt.Errorf(\"cachebench: trace line %d: incomplete or mismatched billed-token budget\", line)\n\t\t\t}\n\t\t}\n\t\tseen[record.RequestID] = true\n\t\trecord.Body = append(json.RawMessage(nil), record.Body...)\n\t\trecords = append(records, record)\n\t}\n\tif err := scanner.Err(); err != nil {\n\t\treturn nil, fmt.Errorf(\"cachebench: read trace: %w\", err)\n\t}\n\tif len(records) == 0 {\n\t\treturn nil, errors.New(\"cachebench: no trace records\")\n\t}\n\treturn records, nil\n}\n\n// NativeRequest reconstructs exact optimizer input captured by v2 or v3 trace.\n// Legacy v1 traces remain readable for observation joins, but cannot drive live\n// replay because they omitted routing and economics inputs.\nfunc (record TraceRecord) NativeRequest() (cacheengine.NativeRequest, error) {\n\tif record.Schema != TraceSchema && record.Schema != TraceSchemaV2 {\n\t\treturn cacheengine.NativeRequest{}, fmt.Errorf(\"cachebench: request %q needs %s or %s for reconstruction\", record.RequestID, TraceSchemaV2, TraceSchema)\n\t}\n\tif !validTraceIdentity(record) || !validTimingBasis(record.TimingBasis) || record.ExpectedRPM <= 0 || record.ExpectedCalls <= 0 || record.PrefixTokens < 0 || !validUniqueJSONObject(record.Body) || record.BodySHA256 != bodyDigest(record.Body) || record.StableSegmentCount < 0 || record.StableSegmentCount > len(record.Prefix) || !validPrefix(record.Prefix) {\n\t\treturn cacheengine.NativeRequest{}, fmt.Errorf(\"cachebench: request %q has incomplete or invalid replay identity\", record.RequestID)\n\t}\n\treturn cacheengine.NativeRequest{\n\t\tScope: record.Scope, Epoch: record.Epoch, PartitionKey: record.PartitionKey,\n\t\tExpectedRequestsPerMinute: record.ExpectedRPM, ExpectedCalls: record.ExpectedCalls,\n\t\tProvider: record.Provider, Model: record.Model, Region: record.Region, Endpoint: record.Endpoint,","sourceCodeStart":324,"sourceCodeEnd":360,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/cacheengine/cachebench/trace.go#L324-L360","documentation":"Returned by ReadTraceJSONLWithLimits when the scanner reached EOF without accumulating any records. Every line was either empty after bytes.TrimSpace or the input was empty entirely, so the parsed record slice has length 0. The library treats a record-less trace as an error because downstream replay/validation code assumes at least one trace record exists.","triggerScenarios":"Passing an empty io.Reader or a file containing only whitespace/newlines to ReadTraceJSONL or ReadTraceJSONLWithLimits. Pointing -trace at a zero-byte file or a file whose lines are all blank. Passing an os.File opened on a path that was truncated by a previous failed write.","commonSituations":"Trace capture job failed midway and left an empty file; CI pipeline passes an artifact path before the artifact is downloaded; shell redirect typo created an empty file (e.g. '> trace.jsonl' run without the producing command); passing bytes.NewReader(nil) in tests.","solutions":["Verify the trace file is non-empty and contains JSONL lines before parsing: check file size and first non-blank line","If the file is unexpectedly empty, re-run the trace capture step that produced it","Guard callers that legitimately may have no data by checking len(records)==0 upstream and skipping the parse entirely"],"exampleFix":"// before\nrecords, err := cachebench.ReadTraceJSONL(f) // f is empty -> error\n\n// after\ninfo, _ := f.Stat()\nif info.Size() == 0 {\n    return fmt.Errorf(\"trace file %s is empty; regenerate it\", path)\n}\nrecords, err := cachebench.ReadTraceJSONL(f)","handlingStrategy":"validation","validationCode":"info, err := f.Stat()\nif err != nil {\n\treturn err\n}\nif info.Size() == 0 {\n\treturn errors.New(\"trace file is empty; skipping parse\")\n}\nrecords, err := cachebench.ReadTraceJSONL(f)","typeGuard":null,"tryCatchPattern":"records, err := cachebench.ReadTraceJSONL(r)\nif err != nil {\n\tif strings.Contains(err.Error(), \"no trace records\") {\n\t\tlog.Print(\"empty trace; nothing to replay\")\n\t\treturn nil, nil\n\t}\n\treturn nil, err\n}","preventionTips":["Make trace producers write atomically (temp file + rename) so failed captures leave no empty artifacts","Assert non-zero size when downloading/receiving traces in CI before invoking replay"],"tags":["go","input-validation","trace-data"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}