{"record":{"id":"0f6a628d278f8368","repo":"wavetermdev/waveterm","slug":"ijson-budget-must-be-non-negative","errorCode":null,"errorMessage":"ijson budget must be non-negative","messagePattern":"ijson budget must be non-negative","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/filestore/blockstore.go","lineNumber":134,"sourceCode":"\tif opts.MaxSize < 0 {\n\t\treturn fmt.Errorf(\"max size must be non-negative\")\n\t}\n\tif opts.Circular && opts.MaxSize <= 0 {\n\t\treturn fmt.Errorf(\"circular file must have a max size\")\n\t}\n\tif opts.Circular && opts.IJson {\n\t\treturn fmt.Errorf(\"circular file cannot be ijson\")\n\t}\n\tif opts.Circular {\n\t\tif opts.MaxSize%partDataSize != 0 {\n\t\t\topts.MaxSize = (opts.MaxSize/partDataSize + 1) * partDataSize\n\t\t}\n\t}\n\tif opts.IJsonBudget > 0 && !opts.IJson {\n\t\treturn fmt.Errorf(\"ijson budget requires ijson\")\n\t}\n\tif opts.IJsonBudget < 0 {\n\t\treturn fmt.Errorf(\"ijson budget must be non-negative\")\n\t}\n\treturn withLock(s, zoneId, name, func(entry *CacheEntry) error {\n\t\tif entry.File != nil {\n\t\t\treturn fs.ErrExist\n\t\t}\n\t\tnow := time.Now().UnixMilli()\n\t\tfile := &WaveFile{\n\t\t\tZoneId:    zoneId,\n\t\t\tName:      name,\n\t\t\tSize:      0,\n\t\t\tCreatedTs: now,\n\t\t\tModTs:     now,\n\t\t\tOpts:      opts,\n\t\t\tMeta:      meta,\n\t\t}\n\t\treturn dbInsertFile(ctx, file)\n\t})\n}","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/filestore/blockstore.go#L116-L152","documentation":"A negative IJsonBudget is meaningless (a budget cannot be less than zero), so MakeFile rejects it during option validation. Note the ordering: the 'requires ijson' check runs first, so this error only appears when IJson is true (or the budget is negative and IJson is true).","triggerScenarios":"Calling MakeFile with opts.IJsonBudget < 0 — e.g. a caller using -1 as an 'unlimited' sentinel or a size computation underflowing.","commonSituations":"Using -1 to mean 'no budget limit' (not supported; use 0); arithmetic like totalBudget - used going negative before the call; unvalidated user input parsed into the budget field.","solutions":["Pass IJsonBudget >= 0; use 0 for 'no explicit budget' semantics.","Normalize sentinels before the call: if budget < 0 { budget = 0 }.","Clamp computed budgets: opts.IJsonBudget = max(0, computed)."],"exampleFix":"// before\nopts := wshrpc.FileOpts{IJson: true, IJsonBudget: -1} // meant 'unlimited'\n// after\nbudget := -1\nif budget < 0 {\n\tbudget = 0\n}\nopts := wshrpc.FileOpts{IJson: true, IJsonBudget: budget}","handlingStrategy":"validation","validationCode":"func validBudget(opts wshrpc.FileOpts) bool { return opts.IJsonBudget >= 0 }\nif !validBudget(opts) {\n\treturn fmt.Errorf(\"ijsonBudget must be >= 0\")\n}","typeGuard":null,"tryCatchPattern":"if err := store.MakeFile(ctx, zoneId, name, meta, opts); err != nil {\n\tif strings.Contains(err.Error(), \"ijson budget must be non-negative\") {\n\t\topts.IJsonBudget = 0\n\t\terr = store.MakeFile(ctx, zoneId, name, meta, opts)\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n}","preventionTips":["Use 0, not -1, for 'no budget limit'.","Clamp computed budgets: budget = max(0, computed).","Validate user input before assigning it to IJsonBudget.","Guard subtraction-based budget math against underflow."],"tags":["validation","filestore","input-validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}