alibaba/open-code-review · error

OCR_LLM_TIMEOUT must be an integer (seconds): %w

Error message

OCR_LLM_TIMEOUT must be an integer (seconds): %w

What it means

OCR_LLM_TIMEOUT is interpreted strictly as an integer number of seconds. parseTimeoutEnv uses strconv.Atoi on the trimmed value; anything non-numeric ("30s", "2m", "") beyond empty produces this wrapped error. Like other global overrides, it fails fast before credential prompts.

Source

Thrown at internal/llm/resolver.go:210

				ep.ExtraHeaders[key] = value
			}
		}
	}
	return ep
}

// parseTimeoutEnv reads and validates the OCR_LLM_TIMEOUT environment variable.
// Returns the parsed duration and true if set, or 0 and false if unset/empty.
// Returns an error for invalid values (non-integer, negative, overflow) to give
// the user clear feedback instead of silently falling back to the default.
func parseTimeoutEnv() (time.Duration, bool, error) {
	raw := strings.TrimSpace(os.Getenv(envOCRLLMTimeout))
	if raw == "" {
		return 0, false, nil
	}
	sec, err := strconv.Atoi(raw)
	if err != nil {
		return 0, false, fmt.Errorf("OCR_LLM_TIMEOUT must be an integer (seconds): %w", err)
	}
	d, err := validateTimeoutSec(sec)
	if err != nil {
		return 0, false, fmt.Errorf("OCR_LLM_TIMEOUT: %w", err)
	}
	return d, true, nil
}

// validateTimeoutSec converts a config-file timeout (in seconds) to time.Duration.
// Returns 0 for zero input (use default). Rejects negative values and overflow.
func validateTimeoutSec(sec int) (time.Duration, error) {
	if sec == 0 {
		return 0, nil
	}
	if sec < 0 {
		return 0, fmt.Errorf("timeout_sec must be non-negative, got %d", sec)
	}
	// Guard against overflow: time.Duration is int64 nanoseconds.

View on GitHub (pinned to 5cf97d0d15)

Solutions

  1. Set OCR_LLM_TIMEOUT to a plain integer of seconds, e.g. 120 instead of "2m"
  2. Remove the unit suffix and any surrounding characters
  3. Unset the variable to use the client default (5 minutes)

Example fix

// before
export OCR_LLM_TIMEOUT="90s"
// after
export OCR_LLM_TIMEOUT=90
Defensive patterns

Strategy: validation

Validate before calling

raw := strings.TrimSpace(os.Getenv("OCR_LLM_TIMEOUT"))
if raw != "" {
    if _, err := strconv.Atoi(raw); err != nil {
        return fmt.Errorf("OCR_LLM_TIMEOUT must be plain seconds integer, got %q", raw)
    }
}

Try / catch

if err != nil && strings.Contains(err.Error(), "OCR_LLM_TIMEOUT must be an integer") {
    return fmt.Errorf("export OCR_LLM_TIMEOUT=90 instead of %q", os.Getenv("OCR_LLM_TIMEOUT"))
}

Prevention

When it happens

Trigger: Setting OCR_LLM_TIMEOUT to a duration-style string ("30s", "1m30s"), a float ("2.5"), or any non-integer while ResolveEndpointWithOptions runs parseEnvOverrides.

Common situations: Users copying Go duration syntax from other tools; assuming the variable accepts "30s" because many CLIs do; a locale or whitespace artifact is handled by TrimSpace, so the usual cause is a unit suffix.

Understand the failure class

Related errors


AI-assisted analysis of alibaba/open-code-review@5cf97d0d15 (2026-09-02). Data as JSON: /api/errors/e19bb83f1514edc8. Report an issue: GitHub.