projectdiscovery/katana · error

capsolver error %s: %s

Error message

capsolver error %s: %s

What it means

capsolver's createTask parses the createTask response and returns 'capsolver error %s: %s' (errorCode, errorDescription) whenever the response's errorId is non-zero. This is the API-level rejection reported by the capsolver service itself, not a transport failure.

Source

Thrown at pkg/engine/headless/captcha/capsolver/capsolver.go:132

	req.Header.Set("Content-Type", "application/json")

	resp, err := s.client.Do(req)
	if err != nil {
		return "", err
	}
	defer func() { _ = resp.Body.Close() }()

	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}

	var result createTaskResponse
	if err := json.Unmarshal(respBody, &result); err != nil {
		return "", err
	}
	if result.ErrorID != 0 {
		return "", fmt.Errorf("capsolver error %s: %s", result.ErrorCode, result.ErrorDescription)
	}
	return result.TaskID, nil
}

func (s *Solver) pollResult(ctx context.Context, taskID string, provider captcha.Provider) (*captcha.Solution, error) {
	ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
	defer cancel()

	ticker := time.NewTicker(defaultPollInterval)
	defer ticker.Stop()

	for {
		select {
		case <-ctx.Done():
			return nil, fmt.Errorf("captcha solve timed out after %s", defaultTimeout)
		case <-ticker.C:
			result, err := s.getTaskResult(ctx, taskID)
			if err != nil {

View on GitHub (pinned to e3e742739c)

Solutions

  1. Read the errorCode/errorDescription in the error and look it up in capsolver's API error docs for the exact cause.
  2. Verify the capsolver API key and account balance; top up or regenerate the key.
  3. Fix task parameters: confirm websiteURL and websiteKey match the target page's captcha configuration.
  4. Confirm the task type (e.g. Enterprise variants) is supported on your capsolver plan.
  5. Fail fast without retry — API rejections are deterministic and retrying the same payload will not help.

Example fix

// before
sol, err := solver.Solve(ctx, info)
if err != nil {
    retry(...) // blindly retries, always fails for bad API key
}
// after
sol, err := solver.Solve(ctx, info)
if err != nil && strings.Contains(err.Error(), "capsolver error ERROR_ZERO_BALANCE") {
    return nil, fmt.Errorf("capsolver account out of balance: %w", err) // alert ops, don't retry
}
Defensive patterns

Strategy: try-catch

Validate before calling

// preflight: validate key format and balance via a cheap call if available
if !strings.HasPrefix(apiKey, "CAP-") { log.Warn("capsolver API key looks malformed") }

Try / catch

sol, err := solver.Solve(ctx, info)
if err != nil {
    if strings.HasPrefix(err.Error(), "capsolver error ") {
        code := parseCapsolverErrorCode(err)
        switch code {
        case "ERROR_ZERO_BALANCE":
            return nil, fmt.Errorf("top up capsolver account: %w", err)
        case "ERROR_KEY_DOES_NOT_EXIST":
            return nil, fmt.Errorf("invalid capsolver API key: %w", err)
        default:
            return nil, err // deterministic API rejection, never retry
        }
    }
    return nil, err
}

Prevention

When it happens

Trigger: POST to /createTask succeeded and returned JSON with errorId != 0 — e.g. ERROR_KEY_DOES_NOT_EXIST, ERROR_ZERO_BALANCE, ERROR_TASK_NOT_SUPPORTED, or invalid task parameters (bad websiteKey/websiteURL).

Common situations: Invalid, expired, or revoked capsolver API key; account out of balance; task type not enabled for the account; wrong siteKey or domain in the task payload; enterprise variants used without an enterprise subscription.

Related errors


AI-assisted analysis of projectdiscovery/katana@e3e742739c (2026-09-03). Data as JSON: /api/errors/88e0b852f182d230. Report an issue: GitHub.