I often write functions in my ~/.vimrc or in ~/.vim/autoload/myfunctions.vim, which when executed result in an error such as:

Error detected while processing function SomeFunction:
line   xx:

Those errors can be read in the output of the Ex command :messages.
Is there a way to populate the quickfix list with them, so that I can type :copen, navigate through them, hit Enter on one, and be automatically redirected to the file and line where an error has been raised?

  • 1
    it should be possible to set the 'errorformat' to the required formatstring, redir the messages command to a temporary file and use :cfile to read and parse the file into a quickfix list. I have never done that however – Christian Brabandt Jan 1 '16 at 16:30
  • @ChristianBrabandt I thought about doing something like this but I don't know all the details of :help error-file-format. That's the solution that I would like the most. If one day I know how to do it, I'll post it here. Thank you for pointing me on the right direction ('errorformat') and confirming it's possible. – saginaw Jan 2 '16 at 12:02
  • 3
    Yeah, errorformat is a little bit like black vodoo ;) – Christian Brabandt Jan 2 '16 at 12:55

A recent question on SO inspired me to develop a function that decodes v:throwpoint that I use in a new logger that fills the quickfix list, and my unit testing framework for vim.

This can be used to enhance romainl's answer in order to decode file+line from error messages.

function! Bar() abort

function! Foo()
    call Bar()
  catch /.*/
    let bt = lh#exception#callstack(v:throwpoint)
    let data = map(copy(bt), '{"filename": v:val.script, "text": "called from here", "lnum": v:val.pos}')
    let data[0].text = v:exception
    call setqflist(data)

Since my initial answer, I've improved the workflow to be able to decode the last error(s) sent as messages. A typical way to use it would be through a command like the following

command! -nargs=? WTF call lh#exception#say_what(<q-args>)

Note: my implementation tries to adapt automatically to the current locale (see :h :lang...) as error messages are localized, and to support some other minor issue (like '#' character not 'isk' list option).


I don't have a complete solution for you but most complex plugins have some kind of logging function for handling errors in a generic way.

A good approach would be to wrap some or all of your code in a try-catch block, parse the exception and populate the quickfix list with useful information.

Here is a very simplistic implementation of the idea:

function Foo()
        call setqflist([{"bufnr":1,"lnum":1,"text":v:exception}])

Pointing to the right file doesn't sound as easy, though.

See :help setqflist(), :help :try, :help v:exception.


I've taken the liberty to build on both @romainl and @LucHermite's answers. I define the Stacktrace function, which can be called both manually and to get a trace for a specific error.

function! MyFunction(...)
  " Stacktrace can be called to get a manual stacktrace
  call Stacktrace()

  " It can also be called to get stacktrace for an error
    " ...
    call Stacktrace()

function! Stacktrace()
  " Get stack and exception
  if empty(v:throwpoint)
      throw 'dummy'
      let l:stack = reverse(split(v:throwpoint, '\.\.'))[1:]
      let l:exception = 'Manual stacktrace'
    let l:stack = reverse(split(v:throwpoint, '\.\.'))
    let l:exception = v:exception

  " Build the quickfix entries
  let l:qflist = []
  let l:files = {}
  for l:func in l:stack
    let [l:name, l:offset] = (l:func =~# '\S\+\[\d')
          \ ? matchlist(l:func, '\(\S\+\)\[\(\d\+\)\]')[1:2]
          \ : matchlist(l:func, '\(\S\+\), line \(\d\+\)')[1:2]

    if l:name =~# '\v(\<SNR\>|^)\d+_'
      let l:sid = matchstr(l:name, '\v(\<SNR\>|^)\zs\d+\ze_')
      let l:name  = substitute(l:name, '\v(\<SNR\>|^)\d+_', 's:', '')
      let l:filename = map(
            \ Command('scriptnames'),
            \ 'split(v:val, "\\v:=\\s+")[1]')[l:sid-1]
      let l:name = l:name
      let l:filename = matchstr(
            \ Command('verbose function ' . l:name)[1],
            \ '.\{-}\s\+\zs\f\+$')

    let l:filename = fnamemodify(l:filename, ':p')
    if filereadable(l:filename)
      if !has_key(l:files, l:filename)
        let l:files[l:filename] = reverse(readfile(l:filename))

      let l:lnum = l:offset + len(l:files[l:filename])
            \ - match(l:files[l:filename], '^\s*fu\%[nction]!\=\s\+' . l:name)
      let l:text = len(l:qflist) == 0 ? l:exception : '#' . len(l:qflist)

      call add(l:qflist, {
            \ 'filename': l:filename,
            \ 'lnum': l:lnum,
            \ 'text': l:text,

  call setqflist(l:qflist)
  execute 'copen' len(l:qflist) + 2
  wincmd p

function! Command(cmd)
  let l:a = @a
    silent! redir @a
    silent! execute a:cmd
    redir END
    let l:res = @a
    let @a = l:a
    return split(l:res, "\n")

enter image description hereI made a Vim plugin which is to jump to Vim script's last error or load errors to current loclist.


If you would like, you can try it.

  • 1
    rbtnn, welcome to Vi and Vim! Btw, I noticed another account with the same name submitted an edit to your answer. Do you need a mod to merge the accounts (are they the same person)? – D. Ben Knoble Feb 9 at 19:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.