Skip to content

Error processing test.finish event: TypeError: finish is not a function #5461

Closed

Description

@incoming-th

Hi,

I believe this is a small fix but I can't figure it from where.

Maybe someone could help to check and guide, or even better already has a solution for this:

Error processing test.finish event:
TypeError: finish is not a function
at EventEmitter. (/.../node_modules/codeceptjs/lib/pause.js:40:5)
at EventEmitter.emit (node:events:529:35)
at Object.emit (/.../node_modules/codeceptjs/lib/event.js:162:28)
at /.../node_modules/codeceptjs/lib/mocha/asyncWrapper.js:74:13
at /.../node_modules/codeceptjs/lib/recorder.js:274:9

Image

Can be reproduced by doing:

recorder.add(Error, () => Promise.reject(new Error(message)));

Thanks!

Activity

  1. kobenguyent commented on Apr 7, 2026

    @kobenguyent
    Collaborator

    noticed this was introduced by #4713

  2. kobenguyent commented on Apr 7, 2026

    @kobenguyent
    Collaborator

    @incoming-th may you try this fix locally?

    Here's why:

    The Bug

    finish is declared as a module-level variable (line 18) but is only assigned inside pauseSession() (line 86), where it's set to the resolve function of a Promise:

    return new Promise(resolve => {
        finish = resolve  // ← only assigned here
        return askForStep()
    })

    The Race Condition

    The event.test.finished listener is registered at line 39 and calls finish() at line 40:

    event.dispatcher.on(event.test.finished, () => {
        finish()  // ← called here
        recorder.session.restore('pause')
        rl.close()
        history.save()
    })

    This will throw TypeError: finish is not a function if event.test.finished fires before pauseSession() has ever been called — because finish is still undefined at that point.

    When Can This Happen?

    • A test finishes very quickly (e.g., skipped, errored early) before recorder.add('Start new session', ...) has had a chance to invoke pauseSession().
    • The pause() function is called, the event listener is registered, but the recorder hasn't yet executed the pauseSession callback by the time the test.finished event fires.

    Fix

    Guard the finish() call with a type check:

    event.dispatcher.on(event.test.finished, () => {
        if (typeof finish === 'function') finish()  // ← safe guard
        recorder.session.restore('pause')
        rl.close()
        history.save()
    })

    The same guard should also be applied to the parseInput call at line 97, which has the same vulnerability.

  3. incoming-th commented on Apr 7, 2026

    @incoming-th
    ContributorAuthor

    @kobenguyent Thanks for looking into this. The safeguard is fine.

  4. added a commit that references this issue on Jun 7, 2026
    8ca768e
  5. added a commit that references this issue on Jun 7, 2026
    4491b3f
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions