{"record":{"id":"9560f6ae28d00e14","repo":"wavetermdev/waveterm","slug":"offset-must-be-non-negative","errorCode":null,"errorMessage":"offset must be non-negative","messagePattern":"offset must be non-negative","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/filestore/blockstore.go","lineNumber":242,"sourceCode":"\t\treturn nil\n\t})\n}\n\nfunc (s *FileStore) WriteFile(ctx context.Context, zoneId string, name string, data []byte) error {\n\treturn withLock(s, zoneId, name, func(entry *CacheEntry) error {\n\t\terr := entry.loadFileIntoCache(ctx)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tentry.writeAt(0, data, true)\n\t\t// since WriteFile can *truncate* the file, we need to flush the file to the DB immediately\n\t\treturn entry.flushToDB(ctx, true)\n\t})\n}\n\nfunc (s *FileStore) WriteAt(ctx context.Context, zoneId string, name string, offset int64, data []byte) error {\n\tif offset < 0 {\n\t\treturn fmt.Errorf(\"offset must be non-negative\")\n\t}\n\treturn withLock(s, zoneId, name, func(entry *CacheEntry) error {\n\t\terr := entry.loadFileIntoCache(ctx)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfile := entry.File\n\t\tif offset > file.Size {\n\t\t\treturn fmt.Errorf(\"offset is past the end of the file\")\n\t\t}\n\t\tpartMap := file.computePartMap(offset, int64(len(data)))\n\t\tincompleteParts := incompletePartsFromMap(partMap)\n\t\terr = entry.loadDataPartsIntoCache(ctx, incompleteParts)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tentry.writeAt(offset, data, false)\n\t\treturn nil","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/filestore/blockstore.go#L224-L260","documentation":"FileStore.WriteAt rejects any negative offset before touching the file. Writes to a block-backed file must start at a non-negative byte offset since the offset is mapped onto fixed-size data parts in the DB. The check happens up front so no lock or cache load occurs for an obviously invalid call.","triggerScenarios":"Calling WriteAt(ctx, zoneId, name, offset, data) with offset < 0, typically because an offset was computed from a subtraction (e.g. size - remaining) that underflowed, or a caller passed -1 as an 'unset' sentinel.","commonSituations":"Arithmetic on int64 offsets overflowing/underflowing, sentinel values like -1 used for 'append' mistakenly, or deserializing offsets from JSON/binary where a missing field defaults to a negative value.","solutions":["Validate offset >= 0 in the caller before invoking WriteAt.","Fix the offset arithmetic that produced the negative value (check subtraction order and underflow).","If appending is intended, compute offset from the current file size via a stat/ReadAt of size 0 instead of a sentinel."],"exampleFix":"// before\nfs.WriteAt(ctx, zone, name, -1, data) // meant 'append'\n// after\nvar offset int64 = 0 // or track the known file size\nif offset < 0 { return fmt.Errorf(\"invalid offset %d\", offset) }\nfs.WriteAt(ctx, zone, name, offset, data)","handlingStrategy":"validation","validationCode":"if offset < 0 { return fmt.Errorf(\"WriteAt: offset %d must be >= 0\", offset) }","typeGuard":"func validOffset(offset int64) bool { return offset >= 0 }","tryCatchPattern":null,"preventionTips":["Never use negative int64 values as sentinels for offsets.","Check subtraction-based offset math for underflow.","Centralize file writes behind a wrapper that validates offset first."],"tags":["filestore","validation","arguments"],"backgroundTag":"invalid-argument-offset-negative","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}