Cannot access __builtins__.open from inside a workflow

Hello,

I’m building a file upload feature using Temporal where the frontend uploads directly to Google Cloud Storage (GCS) via a signed URL. My backend, which uses the Temporal Python SDK, is responsible for generating this URL.

The process is initiated by an agent event, generate_upload_url, which then calls an activity, generate_upload_url_activity, to create the signed URL. To create the GCS client, I need to read a service-account-file.json from the local filesystem.

Initially, I was reading the file directly within my agent’s event handler, which is part of the workflow logic. This resulted in the following error:

{
  "signed_url": "",
  "object_name": "",
  "error": "Cannot access __builtins__.open from inside a workflow. If this is code from a module not used in a workflow or known to only be used deterministically from a workflow, mark the import as pass through."
}

Here is the relevant (and problematic) code from my agent:

src/agents/main_agent.py

@agent.event
async def generate_upload_url(self, input: GenerateUploadUrlInput) -> GenerateUploadUrlOutput:
    """Generate a signed URL for direct client upload to GCS"""
    log.info("--- generate_upload_url event handler started ---")
    try:
        # ... (user_id and logging)
        
        from temporalio import workflow
        from src.activities.cloud_storage import generate_upload_url_activity, GenerateUploadUrlActivityInput
        from datetime import timedelta

        # THIS IS THE PROBLEMATIC LINE
        with open("service-account-file.json", "r") as f:
            service_account_json = f.read()

        activity_input = GenerateUploadUrlActivityInput(
            # ... (other params)
            service_account_json=service_account_json,
        )

        result = await workflow.execute_activity(
            generate_upload_url_activity,
            activity_input,
            schedule_to_close_timeout=timedelta(seconds=60),
        )
        
        # ... (return logic)

And here is the activity that uses the credentials:

src/activities/cloud_storage.py

@activity.defn
async def generate_upload_url_activity(input: GenerateUploadUrlActivityInput) -> GenerateUploadUrlActivityOutput:
    """Activity to generate signed URL for direct client upload"""
    try:
        # ... (logging)
        
        # Load credentials from the input string
        credentials_info = json.loads(input.service_account_json)
        storage_client = storage.Client.from_service_account_info(credentials_info)
        
        # ... (URL generation logic)

My proposed solution is to create a new, separate activity whose only job is to read the service-account-file.json and return its contents as a string. The agent would then call this new activity first and pass the result to the generate_upload_url_activity.

Is this the standard and most secure way to handle this? Or is there a better approach, such as loading the credentials into an environment variable when the worker starts and accessing it from the activity context?

Thank you for your guidance!

Hi @Ale_Garcia

Cannot access builtins.open from inside a workflow

This is the sandbox preventing us to write non-determinstic code

Any IO operation should go into an activity, if it fail it can be retried and the output will be recorded in the event history (so won’t be executed again during workflow replay).

Do all your workers have access to the same filesystem? If not routing the activity to an specific worker can be a good approach for your use-case

Thanks! So I have now the following new issue hope you can check it out.**
What I’ve Done:**

  1. Moved File I/O to an Activity: I created a new activity get_service_account_json_activity that handles the file reading.
  2. Updated the Workflow: The workflow now calls this activity to get the service account JSON before proceeding with URL generation.
  3. Registered the Activity: I added the activity to the worker’s activity list in the worker configuration.

Relevant Code Snippets:

Activity Implementation (src/activities/cloud_storage.py):

@activity.defn
async def get_service_account_json_activity(input: GetServiceAccountJsonInput) -> GetServiceAccountJsonOutput:
    """Activity to read the service account JSON file from disk"""
    try:
        activity.logger.info(f"Reading service account file from {input.file_path}")
        with open(input.file_path, "r") as f:
            service_account_json = f.read()
        return GetServiceAccountJsonOutput(service_account_json=service_account_json)
    except Exception as e:
        activity.logger.error(f"Error reading service account file: {str(e)}")
        return GetServiceAccountJsonOutput(service_account_json="", error=str(e))

Worker Configuration (src/services.py):

worker_config = {
    "workflows": [AutonomousCodingWorkflow, E2BWorkflow],
    "activities": [
        # ... other activities ...
        get_service_account_json_activity,  # Added this
    ],
    "workflow_runner": SandboxedWorkflowRunner(restrictions=restrictions),
}

Current Error: Despite these changes, the activity times out with the error:

{
  "signed_url": "",
  "object_name": "",
  "error": "Activity task timed out"
}

Hi @Ale_Garcia

You may have to increase the timeout for one of your activities, Can you share the workflow history, please remove any payload or PII and the activity options?

Antonio

{
  "events": [
    {
      "eventId": "1",
      "eventTime": "2025-09-19T19:45:52.831788387Z",
      "eventType": "EVENT_TYPE_WORKFLOW_EXECUTION_STARTED",
      "taskId": "1048728",
      "workflowExecutionStartedEventAttributes": {
        "workflowType": {
          "name": "AgentAzlon"
        },
        "taskQueue": {
          "name": "local-restack",
          "kind": "TASK_QUEUE_KIND_NORMAL"
        },
        "input": {
          "payloads": [
            {
              "metadata": {
                "encoding": "binary/null"
              },
              "data": null
            }
          ]
        },
        "workflowExecutionTimeout": "0s",
        "workflowRunTimeout": "0s",
        "workflowTaskTimeout": "10s",
        "originalExecutionRunId": "01996382-fcbf-7c01-a6a4-89bb084097be",
        "identity": "<REDACTED_IDENTITY>",
        "firstExecutionRunId": "01996382-fcbf-7c01-a6a4-89bb084097be",
        "attempt": 1,
        "firstWorkflowTaskBackoff": "0s",
        "memo": {
          "fields": {
            "agent": {
              "metadata": {
                "encoding": "json/plain"
              },
              "data": "<REDACTED_BOOLEAN>"
            },
            "engineId": {
              "metadata": {
                "encoding": "json/plain"
              },
              "data": "<REDACTED_STRING>"
            }
          }
        },
        "searchAttributes": {
          "indexedFields": {
            "engineId": {
              "metadata": {
                "encoding": "json/plain",
                "type": "Keyword"
              },
              "data": "<REDACTED_STRING>"
            }
          }
        },
        "header": {},
        "workflowId": "local-66b13c16-AgentAzlon"
      }
    },
    {
      "eventId": "2",
      "eventTime": "2025-09-19T19:45:52.835201346Z",
      "eventType": "EVENT_TYPE_WORKFLOW_TASK_SCHEDULED",
      "taskId": "1048729",
      "workflowTaskScheduledEventAttributes": {
        "taskQueue": {
          "name": "local-restack",
          "kind": "TASK_QUEUE_KIND_NORMAL"
        },
        "startToCloseTimeout": "10s",
        "attempt": 1
      }
    },
    {
      "eventId": "3",
      "eventTime": "2025-09-19T19:45:52.864468137Z",
      "eventType": "EVENT_TYPE_WORKFLOW_TASK_STARTED",
      "taskId": "1048734",
      "workflowTaskStartedEventAttributes": {
        "scheduledEventId": "2",
        "identity": "<REDACTED_IDENTITY>",
        "requestId": "<REDACTED_REQUEST_ID>",
        "historySizeBytes": "424",
        "workerVersion": {
          "buildId": "<REDACTED_BUILD_ID>"
        }
      }
    },
    {
      "eventId": "4",
      "eventTime": "2025-09-19T19:45:53.301203804Z",
      "eventType": "EVENT_TYPE_WORKFLOW_TASK_COMPLETED",
      "taskId": "1048738",
      "workflowTaskCompletedEventAttributes": {
        "scheduledEventId": "2",
        "startedEventId": "3",
        "identity": "<REDACTED_IDENTITY>",
        "workerVersion": {
          "buildId": "<REDACTED_BUILD_ID>"
        },
        "sdkMetadata": {
          "coreUsedFlags": [
            1,
            3,
            2
          ],
          "sdkName": "temporal-python",
          "sdkVersion": "1.16.0"
        },
        "meteringMetadata": {}
      }
    },
    {
      "eventId": "5",
      "eventTime": "2025-09-19T19:45:53.317562679Z",
      "eventType": "EVENT_TYPE_WORKFLOW_TASK_SCHEDULED",
      "taskId": "1048742",
      "workflowTaskScheduledEventAttributes": {
        "taskQueue": {
          "name": "local-12093-a1424a640a2d4f14b96de1f8a82a5e49",
          "kind": "TASK_QUEUE_KIND_STICKY",
          "normalName": "local-restack"
        },
        "startToCloseTimeout": "10s",
        "attempt": 1
      }
    },
    {
      "eventId": "6",
      "eventTime": "2025-09-19T19:45:53.317590846Z",
      "eventType": "EVENT_TYPE_WORKFLOW_TASK_STARTED",
      "taskId": "1048743",
      "workflowTaskStartedEventAttributes": {
        "scheduledEventId": "5",
        "identity": "<REDACTED_IDENTITY>",
        "requestId": "<REDACTED_REQUEST_ID>",
        "historySizeBytes": "655",
        "workerVersion": {
          "buildId": "<REDACTED_BUILD_ID>"
        }
      }
    },
    {
      "eventId": "7",
      "eventTime": "2025-09-19T19:45:53.345374513Z",
      "eventType": "EVENT_TYPE_WORKFLOW_TASK_COMPLETED",
      "taskId": "1048744",
      "workflowTaskCompletedEventAttributes": {
        "scheduledEventId": "5",
        "startedEventId": "6",
        "identity": "<REDACTED_IDENTITY>",
        "workerVersion": {
          "buildId": "<REDACTED_BUILD_ID>"
        },
        "sdkMetadata": {},
        "meteringMetadata": {}
      }
    },
    {
      "eventId": "8",
      "eventTime": "2025-09-19T19:45:53.346041263Z",
      "eventType": "EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_ACCEPTED",
      "taskId": "1048745",
      "workflowExecutionUpdateAcceptedEventAttributes": {
        "protocolInstanceId": "<REDACTED_PROTOCOL_INSTANCE_ID>",
        "acceptedRequestMessageId": "<REDACTED_ACCEPTED_REQUEST_MESSAGE_ID>",
        "acceptedRequestSequencingEventId": "5",
        "acceptedRequest": {
          "meta": {
            "updateId": "<REDACTED_UPDATE_ID>",
            "identity": "<REDACTED_IDENTITY>"
          },
          "input": {
            "header": {},
            "name": "generate_upload_url",
            "args": {
              "payloads": [
                {
                  "metadata": {
                    "encoding": "json/plain"
                  },
                  "data": "<REDACTED_PAYLOAD>"
                }
              ]
            }
          }
        }
      }
    },
    {
      "eventId": "9",
      "eventTime": "2025-09-19T19:45:53.346438971Z",
      "eventType": "EVENT_TYPE_ACTIVITY_TASK_SCHEDULED",
      "taskId": "1048746",
      "activityTaskScheduledEventAttributes": {
        "activityId": "1",
        "activityType": {
          "name": "get_service_account_json_activity"
        },
        "taskQueue": {
          "name": "local-restack",
          "kind": "TASK_QUEUE_KIND_NORMAL"
        },
        "header": {},
        "input": {
          "payloads": [
            {
              "metadata": {
                "encoding": "json/plain"
              },
              "data": "<REDACTED_PAYLOAD>"
            }
          ]
        },
        "scheduleToCloseTimeout": "300s",
        "scheduleToStartTimeout": "300s",
        "startToCloseTimeout": "45s",
        "heartbeatTimeout": "0s",
        "workflowTaskCompletedEventId": "7",
        "retryPolicy": {
          "initialInterval": "1s",
          "backoffCoefficient": 2,
          "maximumInterval": "30s",
          "maximumAttempts": 3
        },
        "useWorkflowBuildId": true,
        "priority": {}
      }
    },
    {
      "eventId": "10",
      "eventTime": "2025-09-19T19:50:53.358944304Z",
      "eventType": "EVENT_TYPE_ACTIVITY_TASK_TIMED_OUT",
      "taskId": "1048751",
      "activityTaskTimedOutEventAttributes": {
        "failure": {
          "message": "activity ScheduleToStart timeout",
          "source": "Server",
          "timeoutFailureInfo": {
            "timeoutType": "TIMEOUT_TYPE_SCHEDULE_TO_START"
          }
        },
        "scheduledEventId": "9",
        "retryState": "RETRY_STATE_NON_RETRYABLE_FAILURE"
      }
    },
    {
      "eventId": "11",
      "eventTime": "2025-09-19T19:50:53.359537804Z",
      "eventType": "EVENT_TYPE_WORKFLOW_TASK_SCHEDULED",
      "taskId": "1048752",
      "workflowTaskScheduledEventAttributes": {
        "taskQueue": {
          "name": "local-12093-a1424a640a2d4f14b96de1f8a82a5e49",
          "kind": "TASK_QUEUE_KIND_STICKY",
          "normalName": "local-restack"
        },
        "startToCloseTimeout": "10s",
        "attempt": 1
      }
    },
    {
      "eventId": "12",
      "eventTime": "2025-09-19T19:50:53.367895263Z",
      "eventType": "EVENT_TYPE_WORKFLOW_TASK_STARTED",
      "taskId": "1048756",
      "workflowTaskStartedEventAttributes": {
        "scheduledEventId": "11",
        "identity": "<REDACTED_IDENTITY>",
        "requestId": "<REDACTED_REQUEST_ID>",
        "historySizeBytes": "1676",
        "workerVersion": {
          "buildId": "<REDACTED_BUILD_ID>"
        }
      }
    },
    {
      "eventId": "13",
      "eventTime": "2025-09-19T19:50:53.432215054Z",
      "eventType": "EVENT_TYPE_WORKFLOW_TASK_COMPLETED",
      "taskId": "1048760",
      "workflowTaskCompletedEventAttributes": {
        "scheduledEventId": "11",
        "startedEventId": "12",
        "identity": "<REDACTED_IDENTITY>",
        "workerVersion": {
          "buildId": "<REDACTED_BUILD_ID>"
        },
        "sdkMetadata": {},
        "meteringMetadata": {}
      }
    },
    {
      "eventId": "14",
      "eventTime": "2025-09-19T19:50:53.433297679Z",
      "eventType": "EVENT_TYPE_WORKFLOW_EXECUTION_UPDATE_COMPLETED",
      "taskId": "1048761",
      "workflowExecutionUpdateCompletedEventAttributes": {
        "meta": {
          "updateId": "<REDACTED_UPDATE_ID>",
          "identity": "<REDACTED_IDENTITY>"
        },
        "acceptedEventId": "8",
        "outcome": {
          "success": {
            "payloads": [
              {
                "metadata": {
                  "encoding": "json/plain"
                },
                "data": {
                  "signed_url": "<REDACTED_SIGNED_URL>",
                  "object_name": "<REDACTED_OBJECT_NAME>",
                  "error": "<REDACTED_ERROR_MESSAGE>"
                }
              }
            ]
          }
        }
      }
    }
  ]
}

following up on my previous questions about temporal and restack sandbox issues. I’ve successfully implemented a microservice solution to bypass the GCS sandbox restrictions, but now I’m stuck on accessing the Temporal Web UI for monitoring workflows.

Quick Context:

  • Restack version: >=0.0.87, <1.0.0
  • Running locally with uv run dev on macOS
  • No Docker, using local processes
  • Backend runs fine, workflows execute, but no UI access

Current Config:

# src/client.py
connection_options = CloudConnectionOptions(
    engine_id=RESTACK_ENGINE_ID,
    api_key=RESTACK_ENGINE_API_KEY,
    address=os.getenv("TEMPORAL_HOST") or "engine.restack.io",
    api_address=RESTACK_ENGINE_API_ADDRESS,
)

# src/services.py
restrictions = SandboxRestrictions.default.with_passthrough_modules(
    "os", "shutil", "subprocess", "http.client", "google.auth", "google.cloud.storage",
    "urllib", "urllib3", "requests", "ssl", "socket", "http", "json", "base64"
)

worker_config = {
    "task_queue": "local-restack",
    "workflows": [AutonomousCodingWorkflow, E2BWorkflow],
    "activities": [/* ... including generate_upload_url_activity */],
    "workflow_runner": SandboxedWorkflowRunner(restrictions=restrictions),
}

Environment:

RESTACK_ENGINE_ID=your_engine_id
RESTACK_ENGINE_API_KEY=your_api_key
RESTACK_ENGINE_ADDRESS=engine.restack.io
# No TEMPORAL_HOST or UI-specific variables

Issue: Tried ports 8080, 8088, 8233 - all connection refused. No UI endpoint in logs. Is the UI not included by default, or do I need to configure/run a separate Temporal server?

Questions:

  1. Does Restack include a Temporal Web UI, or do I need to run Temporal server separately?
  2. If separate, what’s the correct way to configure it for Restack?
  3. Any specific environment variables or config needed to enable UI access?