Skip to content

Comments

fix: handle ServiceConflictError when reusing Actor across sequential context#804

Merged
vdusek merged 4 commits intoapify:masterfrom
Mantisus:fix-sequential-context
Feb 23, 2026
Merged

fix: handle ServiceConflictError when reusing Actor across sequential context#804
vdusek merged 4 commits intoapify:masterfrom
Mantisus:fix-sequential-context

Conversation

@Mantisus
Copy link
Collaborator

Description

  • handle ServiceConflictError when reusing Actor across sequential context

Issues

Testing

  • Added new unit test

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes ServiceConflictError when reusing Actor across sequential async context manager blocks. The issue occurred when mixing Actor (singleton proxy) and Actor() (new instances) in sequential contexts, as new instances would try to register services in the global service_locator that were already set by previous instances.

Changes:

  • Added error handling in event_manager property to catch ServiceConflictError and reuse existing event manager from service_locator
  • Added parametrized test covering all four combinations of Actor/Actor() usage in sequential contexts

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/apify/_actor.py Wraps service_locator.set_event_manager() in try/except to handle ServiceConflictError and reuse existing event manager
tests/unit/actor/test_actor_lifecycle.py Adds parametrized test for all Actor/Actor() combinations in sequential contexts

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@Mantisus Mantisus requested review from Pijukatel and vdusek February 19, 2026 00:30
Copy link
Contributor

@Pijukatel Pijukatel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not so sure about the whole issue. I am afraid that allowing this implicitly leads to more edge cases. I would rather be explicit about such use case. Why not solve the actual issue (problem with example-code-runner-py running Actor() twice due to its unique use case)

apify/crawlee-python#1759
and
https://github.com/apify/example-code-runner-py/compare/master...reset-service-locator-if-custom-actor

@Mantisus
Copy link
Collaborator Author

Perhaps @vlada knows of some additional use cases so that we can better shape the expected behavior.

I followed the general pattern for resources in service_locator. But if the second context manager is expected to be opened with clean resources, then service_locator should be completely cleared when the Actor's context manager is closed.

@janbuchar
Copy link
Contributor

Why not solve the actual issue (problem with example-code-runner-py running Actor() twice due to its unique use case)

apify/crawlee-python#1759 and apify/example-code-runner-py@master...reset-service-locator-if-custom-actor

Now imagine that somebody copies a snippet with repeated async with Actor: statements into their codebase and it stops working because the code depends on the example-code-runner-py. That aside, I think that the example code runner is just one example (haha), perhaps there are other valid use cases for this.

@Pijukatel
Copy link
Contributor

Now imagine that somebody copies a snippet with repeated async with Actor: statements into their codebase and it stops working because the code depends on the example-code-runner-py.

Do we have such a snippet somewhere? example-code-runner-py is private, so I hope nobody ever gets the idea about repeated async with Actor: being some good pattern.

As far as I know, we do not advertise this usage anywhere, or?

@janbuchar
Copy link
Contributor

Now imagine that somebody copies a snippet with repeated async with Actor: statements into their codebase and it stops working because the code depends on the example-code-runner-py.

Do we have such a snippet somewhere? example-code-runner-py is private, so I hope nobody ever gets the idea about repeated async with Actor: being some good pattern.

As far as I know, we do not advertise this usage anywhere, or?

Probably not, doesn't mean that nobody will write one for whatever reason.

@Pijukatel Pijukatel requested review from Pijukatel and janbuchar and removed request for Pijukatel February 20, 2026 13:04
Pijukatel

This comment was marked as outdated.

@Pijukatel Pijukatel dismissed their stale review February 20, 2026 13:11

Without an actual use case outside of our own Actor, I am not really able to properly review the implementation.

@janbuchar
Copy link
Contributor

@Pijukatel

Without an actual use case outside of our own Actor, I am not really able to properly review the implementation.

Refer to the linked issue and make sure that the PR makes the four use scenarios outlined there work as expected. Or feel free to debate what the expected results should be, if you disagree with the issue.

@vdusek
Copy link
Contributor

vdusek commented Feb 20, 2026

@Pijukatel The point of this issue is that either all four use cases (see the issue) should work, or none of them should 🙂.

Copy link
Contributor

@vdusek vdusek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please add an E2E test that will test all cases?

I'm thinking of something like this:

async with Actor:
    Actor.exit_process = False
    Actor.log.info('Hello world')

# Actor after Actor

async with Actor:
    Actor.exit_process = False
    Actor.log.info('Hello world')

# Actor() after Actor

async with Actor(exit_process=False) as actor:
    actor.log.info('Hello world')

# Actor() after Actor()

async with Actor(exit_process=False) as actor:
    actor.log.info('Hello world')

# Actor after Actor()

async with Actor:
    Actor.exit_process = False
    Actor.log.info('Hello world')

Copy link
Contributor

@vdusek vdusek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other than that, it works, and it's LGTM from my side, just please add the mentioned E2E test.

Copy link
Contributor

@vdusek vdusek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested the E2E locally, passing, I believe we can merge it.

@vdusek vdusek merged commit 9e5078f into apify:master Feb 23, 2026
27 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make Actor consistently reusable without raising ServiceConflictError

4 participants