{"record":{"id":"f1211711f6c3fbd4","repo":"kovidgoyal/kitty","slug":"custom-processor-v-produced-invalid-mark-output","errorCode":null,"errorMessage":"Custom processor %#v produced invalid mark output with error: %w","messagePattern":"Custom processor %#v produced invalid mark output with error: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kittens/hints/marks.go","lineNumber":691,"sourceCode":"\t\t\tvar e *exec.ExitError\n\t\t\tif errors.As(err, &e) && e.ExitCode() == 2 {\n\t\t\t\terr = run_basic_matching()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tgoto process_answer\n\t\t\t} else {\n\t\t\t\treturn \"\", nil, nil, fmt.Errorf(\"Failed to run custom processor %#v with error: %w\\n%s\", opts.CustomizeProcessing, err, stderr.String())\n\t\t\t}\n\t\t}\n\t\tans = make([]Mark, 0, 32)\n\t\terr = json.Unmarshal(stdout.Bytes(), &ans)\n\t\tif err != nil {\n\t\t\treturn \"\", nil, nil, fmt.Errorf(\"Failed to load output from custom processor %#v with error: %w\", opts.CustomizeProcessing, err)\n\t\t}\n\t\terr = adjust_python_offsets(sanitized_text, ans)\n\t\tif err != nil {\n\t\t\treturn \"\", nil, nil, fmt.Errorf(\"Custom processor %#v produced invalid mark output with error: %w\", opts.CustomizeProcessing, err)\n\t\t}\n\t} else if opts.Type == \"hyperlink\" {\n\t\tans = hyperlinks\n\t} else if opts.Type == \"word\" {\n\t\tans = mark_words(sanitized_text, opts)\n\t} else {\n\t\terr = run_basic_matching()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\nprocess_answer:\n\tif len(ans) == 0 {\n\t\treturn \"\", nil, nil, &ErrNoMatches{Type: opts.Type, Pattern: used_pattern}\n\t}\n\tlargest_index := ans[len(ans)-1].Index\n\toffset := max(0, opts.HintsOffset)\n\tif opts.PrefixFree {","sourceCodeStart":673,"sourceCodeEnd":709,"githubUrl":"https://github.com/kovidgoyal/kitty/blob/6d5d0c440603ad9bdf6dcd599f73f6dde21acb44/kittens/hints/marks.go#L673-L709","documentation":"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.","triggerScenarios":"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).","commonSituations":"Processor computing offsets on the raw text while kitten passes sanitized text; off-by-one bugs; treating tabs/escape sequences differently.","solutions":["Compute mark offsets against the exact text passed in on stdin, after kitten's sanitization","Clamp all start/end indices to [0, len(text)] and ensure start <= end","Add unit tests in kitty's hints tests (see TestPrefixFreeHints) comparing offsets against the input"],"exampleFix":"# before\nmark['start'] = raw_index\n# after\nmark['start'] = min(max(raw_index, 0), len(sanitized_text))","handlingStrategy":"validation","validationCode":"for _, m := range marks {\n    if m.Start < 0 || m.End > len(text) || m.Start > m.End { return errors.New(\"mark out of range\") }\n}","typeGuard":null,"tryCatchPattern":"Catch 'produced invalid mark output' and re-run with builtin matcher; log offending offsets.","preventionTips":["Compute offsets against the exact sanitized text from stdin","Write unit tests comparing offsets with kitten's hints test suite"],"tags":["kitty","hints","offsets","custom-processor"],"backgroundTag":"index-out-of-range","analyzedSha":"6d5d0c440603ad9bdf6dcd599f73f6dde21acb44","analyzedAt":"2026-08-27T14:20:20.142Z","schemaVersion":2},"datasetVersion":"2026-08-27T19:17:21.184Z"}