JuliusBrussee/caveman · error

rewriter: theta must not be negative, got

Error message

rewriter: theta must not be negative, got %d

What it means

rewriter.New accepts cfg.Theta as the rewriting threshold; a zero value is replaced by DefaultTheta, but a negative value is rejected outright. Theta is a sampling/threshold parameter and a negative value has no meaning, so the constructor refuses to build a Client with one.

Solutions

  1. Pass a non-negative theta, or leave it 0/omitted so DefaultTheta is applied
  2. Map "unset" sentinels (like -1) to 0 before constructing Config
  3. Validate the parsed config value (>= 0) at load time with a clear message
  4. Check sign conventions if porting config from another tool

Example fix

// before
cfg := rewriter.Config{Provider: "openai", Model: "m", Theta: -1} // error
// after
theta := cfgRaw.Threshold
if theta < 0 {
    theta = 0 // 0 means DefaultTheta
}
cfg := rewriter.Config{Provider: "openai", Model: "m", Theta: theta}
Defensive patterns

Strategy: validation

Validate before calling

if cfg.Theta < 0 {
    return fmt.Errorf("theta must be >= 0 (0 selects DefaultTheta=%d), got %d", rewriter.DefaultTheta, cfg.Theta)
}

Try / catch

client, err := rewriter.New(cfg)
if err != nil {
    if strings.Contains(err.Error(), "theta must not be negative") {
        cfg.Theta = 0 // fall back to DefaultTheta
        client, err = rewriter.New(cfg)
    }
    if err != nil { return err }
}

Prevention

When it happens

Trigger: Calling rewriter.New(Config{Theta: <0}) — e.g. Theta: -1 — typically because the field was populated from user input, a config file, or an int that was defaulted with a sentinel of -1 meaning "unset".

Common situations: Config files using -1 as "use default"; arithmetic producing negative thresholds; copying a config from another tool with different sign conventions; test validation paths (TestNewValidation).

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/17208ad6b60b01fa. Report an issue: GitHub.

Appendix: source

Thrown at rewriter/rewriter.go:144

// gate was designed for.
func New(cfg Config) (*Client, error) {
	provider := strings.ToLower(strings.TrimSpace(cfg.Provider))
	if provider != providerAnthropic && provider != providerOpenAI {
		return nil, fmt.Errorf("rewriter: unsupported provider %q", cfg.Provider)
	}
	model := strings.TrimSpace(cfg.Model)
	if model == "" {
		return nil, errors.New("rewriter: model is required")
	}
	if strings.TrimSpace(cfg.APIKey) == "" {
		return nil, errors.New("rewriter: api key is required")
	}
	theta := cfg.Theta
	if theta == 0 {
		theta = DefaultTheta
	}
	if theta < 0 {
		return nil, fmt.Errorf("rewriter: theta must not be negative, got %d", theta)
	}
	doer := cfg.HTTPDoer
	if doer == nil {
		doer = http.DefaultClient.Do
	}
	return &Client{provider: provider, model: model, apiKey: cfg.APIKey, theta: theta, doer: doer}, nil
}

// Rewrite condenses req.StepBytes, or explains why it did not.
//
// Gate 1 (invocation) skips the API call entirely when the step is at or under
// θ tokens. Gate 2 (application) plus the fidelity checks in Accept decide
// whether the completion may replace the original. Only an accepted result
// carries bytes; every other path returns an empty Rewritten and a reason.
func (c *Client) Rewrite(ctx context.Context, req Request) (Result, error) {
	originalTokens := countTokens(req.StepBytes)
	if originalTokens <= c.theta {
		return Result{RejectReason: reasonBelowTheta}, nil

View on GitHub (pinned to 3ee70a1026)