This page is not targeted for people that are 'just getting started' with using actionlib. Instead, it describes the underlying mechanisms through which action clients & servers interact with one another.

If you only want to use the SimpleActionClient or SimpleActionServer, it is not necessary to understand the concepts on this page. However, understanding these concepts provides insight that is useful for debugging client/server interactions or for implementing alternate client/server policies, if the simple client & server aren't descriptive enough.

High Level Client/Server Interaction

Server Description

Server State Machine

  • Goals are initiated by an ActionClient. Once a goal is received by an ActionServer, the ActionServer creates a state machine to track the status of the goal:


Note that this state machine tracks an individual goal, and not the ActionServer itself. Thus, there is a state machine for each goal in the system.

Server Transitions

  • The majority of these state transitions are triggered by the server implementer, using a small set of possible commands:
    • setAccepted - After inspecting a goal, decide to start processing it

    • setRejected - After inspecting a goal, decide to never process it because it is an invalid request (out of bounds, resources not available, invalid, etc)

    • setSucceeded - Notify that goal has been successfully processed

    • setAborted - Notify that goal encountered an error during processsing, and had to be aborted

    • setCanceled - Notify that goal is no longer being processed, due to a cancel request

    The action client can also asynchronously trigger state transitions:
    • CancelRequest: The client notifies the action server that it wants the server to stop processing the goal.

Server States
  • Intermediate States
    • Pending - The goal has yet to be processed by the action server

    • Active - The goal is currently being processed by the action server

    • Recalling - The goal has not been processed and a cancel request has been received from the action client, but the action server has not confirmed the goal is canceled

    • Preempting - The goal is being processed, and a cancel request has been received from the action client, but the action server has not confirmed the goal is canceled

    Terminal States
    • Rejected - The goal was rejected by the action server without being processed and without a request from the action client to cancel

    • Succeeded - The goal was achieved successfully by the action server

    • Aborted - The goal was terminated by the action server without an external request from the action client to cancel

    • Recalled - The goal was canceled by either another goal, or a cancel request, before the action server began processing the goal

    • Preempted - Processing of the goal was canceled by either another goal, or a cancel request sent to the action server

Concurrency Issues
  • setAccepted-CancelRequest vs CancelRequest-setAccepted:
    It is somewhat unintuitive that an action server can setAccepted a goal even after it has received a CancelRequest. This is because of a race condition with a CancelRequest being processed asynchronously. That is, since something other than the server implementer's code is triggering a transition, the server implementer can't be certain whether they're in a [PENDING] or [RECALLING] state.

Client Description

Client State Machine

  • In actionlib, we treat the server state machine as the primary machine, and then treat the client state machine as a secondary/coupled state machine that tries to track the server's state:


Client Transitions

  • Server triggered Transitions
    • Reported [State]: Since the client is trying to track the server's state, most transitions are triggered by the server reporting its state to the ActionClient.

    • Receive Result Message: In this case, the server sends a result message to the client. Receiving a result will always signal the end of tracking a goal.

    Client Triggered Transitions
    • Cancel Goal: Request the server to stop processing this goal

    "Skipping" States
    • Given our ROS based transport layer, it is possible that the client does not receive all of the state updates from the server. Thus, we must allow the client state machine to 'skip past' server triggered states.
      • Example: If the client is in [WAITING FOR GOAL ACK], and receives a [PREEMPTED] status update from the server, the client state can skip past [ACTIVE] , and transition directly to [WAITING FOR RESULT]

    • Since multiple action clients can connect to a single action server, it is possible for a second client to cancel a goal sent by the first client. Thus, it is valid for the client to transition from [PENDING] to [RECALLING] if a [RECALLING] state is received from the server.

Action Interface & Transport Layer

  • The action client and server communicate with each other using a predefined action protocol. This action protocol relies on ROS topics in a specified ROS namespace in order to transport messages.


  • ROS Messages
    • goal - Used to send new goals to servers

    • cancel - Used to send cancel requests to servers

    • status - Used to notify clients on the current state of every goal in the system.

    • feedback - Used to send clients periodic auxiliary information for a goal.

    • result - Used to send clients one-time auxiliary information upon completion of a goal

Data Association and Goal IDs

  • A Goal ID is a string field that is used in all the messages in the action interface. This provides the action server and client a robust way to associate messages being transported over ROS with the specific goals being processed. The Goal ID is generally a combination of a node name, counter, and timestamp. Note: The format of the goal ID is still unstable, so users should never parse goal IDs, or rely on their formatting.

The Messages

goal topic: Sending Goals

  • The goal topic uses an autogenerated ActionGoal message (example: actionlib/TestActionGoal), and is used to send new goals to the action server. Essentially, the ActionGoal message wraps a goal message, and bundles it with a Goal ID.

    When sending a goal, the action client generally generates both a unique Goal ID and a timestamp. However, it is possible that naive (aka dumb) clients might leave either of these empty. If so, the action server will populate them.

    • Empty stamp: Upon receipt by action server, stamp is set to now()

    • Empty id: Upon receipt by action server, id is auto-generated. Note that this ID is not very useful since, the action client doesn't have any way to know the ID that the server generated for its goal.

cancel topic: Cancelling Goals

  • The cancel topic uses actionlib_msgs/GoalID messages, and lets action clients send cancel requests to an action server. Each cancel message has a timestamp and goal ID, and how these message fields are populated will affect which goals are canceled:

  • cancel_policy.png

status topic: Server goal state updates

  • The status topic uses actionlib_msgs/GoalStatusArray, and gives action clients server goal status information about every goal currently being tracked by the action server. This is sent by the action server at some fixed rate (generally 10 Hz), and is also sent asynchronously on any server goal state transition.

    A goal is tracked by the action server until it reaches a terminal state. However, to increase communication robustness, the server publishes status for this goal for a few more seconds after reaching a terminal state.

feedback topic: Asynchronous goal information

  • The feedback topic uses an autogenerated ActionFeedback message (example: todo), and provides server implementers a way to send periodic updates to action clients during the processing of a goal. Since ActionFeedback has a goal ID, the action client can determine if it should use or throw away feedback messages that it receives. Sending feedback is completely optional.

result topic: Goal information upon completion

  • The result topic uses an autogenerated ActionResult message (example: actionlib/TestActionResult), and provides server implementers a way to send information to action clients upon completion of a goal. Since ActionResult has a goal ID, the action client can determine if it should use or throw away the result messages. Although the result can be an empty message, it is a required part of the action interface, and must always be sent on completion of a goal. Thus, a result must be sent on a transition to any Terminal State (Rejected, Recalled, Preempted, Aborted, Succeeded)


Simple Action Client

  • In general, high level applications and executives only care whether a goal is being processed, or if it's complete. They very rarely care about all the intermediate states. The Simple Action Client factors the original client state machine into three states: Pending, Active, & Done


Client State Ambiguities

  • Note that the client state alone is not enough to determine the simple client state. However, this is easily resolved by looking at the client state transitions. If a client state transition doesn't cross between any of the simple client states, then the simple client state is not updated

    Example: if the client transitions from RECALLING to WAITING FOR RESULT, the simple client state will still remain PENDING.

Multiple Goal Policy

  • For simplicity, the Simple Action Client tracks only one goal at a time. When a user sends a goal with the simple client, it disables all callbacks associated with the previous goal and also stops tracking its state. Note that it does not cancel the previous goal!

Threading Model (C++)

  • Upon construction of the simple action client, the user decides whether or not to spin up an extra thread.

    • No extra thread (Recommended)

      • All subscribers in the action client are registered with the global callback queue.
      • The user's action callbacks are called from within ros::spin(). Thus, blocking in the user's action callbacks will prevent the global callback queue from being serviced.
    • Spins up a thread

      • All subscribers in the action client are registered an a callback queue, separate from the global callback queue. This queue is serviced by the spun up thread.
      • The user's action callbacks are called from the spun up thread. Although blocking in action callbacks won't prevent other ROS messages from being serviced, it is still a bad idea, since status, feedback, and result messages for this action cannot be serviced.
      • The one (and only?) advantange to spinning up the extra thread is that a user might be able to avoid calling ros::spin() in their app.

Simple Action Server

Many action servers follow a similar pattern where only one goal can be active at a time and each new goal preempts the previous one. The simple action server is a wrapper around the action server designed to enforce this simple policy for processing goals.


Upon reception of a new goal from an action client, the simple action server moves that goal into its pending slot. If a goal already occupies the pending slot, the simple action server sets that goal to canceled and replaces it with the goal that came in over the wire.


Once a new goal is received by the simple action server and is moved into the pending slot, the user of the simple action server is notified that a new goal is available. This notification occurs in one of two ways as described in the Notificaton of Goals section below. Upon receiving notification, the user can accept the goal which causes the goal in the pending slot to move to the current goal slot, and allows the user to modify the state machine associated with the newly accepted goal.

Notification of Goals

There are two ways a user can receive notification that a new goal has been received by the simple action server:

  • Callback Notification: Here the user registers a callback with the simple action server on construction that is called when a new goal has moved into the pending slot of the simple action server. The user may accept the new goal in the callback, or signal another thread to accept the goal when it is ready.

  • Polling Notification: Here the user asks the simple action server explicitly whether a new goal is available. The simple action server answers this query based on whether a new goal has moved into the pending slot since the last time a new goal query was made.

Threading Model (C++)

Upon construction of the simple action server, the user decides whether or not to spin up an extra thread to allow for taking long running actions in a goal callback.

  • No extra thread (Recommended)

    • Any actions taken in a callback received for a new goal should not be long running. The user may, of course, signal another thread to do work, but should not block.
    • Alternatively, the user can use a polling implementation to check for the availability of new goals and avoid callbacks altogether.
  • Spins up a thread

    • A separate thread is spawned to allow the user to perform long running or blocking actions in a callback received when a new goal is available. Within this callback, the user can also poll the simple action server to check if a new goal is available.
    • The advantage of the simple action server spinning a thread for a user is that the user doesn't have to deal with the overhead of managing another thread. However, it is important for the user to be aware that this thread exists so that they follow standard thread safety conventions such as locking.

Wiki: actionlib/DetailedDescription (last edited 2019-07-22 07:13:08 by teddyluo)