kovidgoyal/kitty · error

Custom processor %#v produced invalid mark output with error

Error message

Custom processor %#v produced invalid mark output with error: %w

What it means

After JSON parsing succeeded, adjust_python_offsets rejected the marks: indices were out of range or inconsistent with the sanitized screen text. Custom processor mark offsets must point into the text it was given.

Solutions

  1. Compute mark offsets against the exact text passed in on stdin, after kitten's sanitization
  2. Clamp all start/end indices to [0, len(text)] and ensure start <= end
  3. Add unit tests in kitty's hints tests (see TestPrefixFreeHints) comparing offsets against the input

Example fix

# before
mark['start'] = raw_index
# after
mark['start'] = min(max(raw_index, 0), len(sanitized_text))
Defensive patterns

Strategy: validation

Validate before calling

for _, m := range marks {
    if m.Start < 0 || m.End > len(text) || m.Start > m.End { return errors.New("mark out of range") }
}

Try / catch

Catch 'produced invalid mark output' and re-run with builtin matcher; log offending offsets.

Prevention

When it happens

Trigger: Marks with start/end beyond the input text length, negative indices, or offsets computed against different text (e.g. before kitten's sanitization or using code points vs bytes inconsistently).

Common situations: Processor computing offsets on the raw text while kitten passes sanitized text; off-by-one bugs; treating tabs/escape sequences differently.

Related errors


AI-assisted analysis of kovidgoyal/kitty@6d5d0c4406 (2026-08-27). Data as JSON: /api/errors/f1211711f6c3fbd4. Report an issue: GitHub.

Appendix: source

Thrown at kittens/hints/marks.go:691

			var e *exec.ExitError
			if errors.As(err, &e) && e.ExitCode() == 2 {
				err = run_basic_matching()
				if err != nil {
					return
				}
				goto process_answer
			} else {
				return "", nil, nil, fmt.Errorf("Failed to run custom processor %#v with error: %w\n%s", opts.CustomizeProcessing, err, stderr.String())
			}
		}
		ans = make([]Mark, 0, 32)
		err = json.Unmarshal(stdout.Bytes(), &ans)
		if err != nil {
			return "", nil, nil, fmt.Errorf("Failed to load output from custom processor %#v with error: %w", opts.CustomizeProcessing, err)
		}
		err = adjust_python_offsets(sanitized_text, ans)
		if err != nil {
			return "", nil, nil, fmt.Errorf("Custom processor %#v produced invalid mark output with error: %w", opts.CustomizeProcessing, err)
		}
	} else if opts.Type == "hyperlink" {
		ans = hyperlinks
	} else if opts.Type == "word" {
		ans = mark_words(sanitized_text, opts)
	} else {
		err = run_basic_matching()
		if err != nil {
			return
		}
	}
process_answer:
	if len(ans) == 0 {
		return "", nil, nil, &ErrNoMatches{Type: opts.Type, Pattern: used_pattern}
	}
	largest_index := ans[len(ans)-1].Index
	offset := max(0, opts.HintsOffset)
	if opts.PrefixFree {

View on GitHub (pinned to 6d5d0c4406)