Execution Environment Limitations
Understanding the constraints and restrictions of the Invoke sandbox execution environment.
File System
Ephemeral File System Access
The sandbox has full read/write access to the file system, but all writes are ephemeral — any files created or modified during an invocation are discarded when the invocation ends.
Note: Ephemeral storage is useful for temporary files within a single invocation (e.g., building a zip in memory, writing intermediary data). For data that must persist across invocations, use the KV Store.
- JavaScript
- TypeScript
- C#
import fs from 'fs'
export default async function handler(req, res) {
// ✅ Works — file exists for this invocation only
fs.writeFileSync('/tmp/data.txt', 'content')
const content = fs.readFileSync('/tmp/data.txt', 'utf8')
// ✅ For persistent data, use KV store
await kv.set('data', 'content')
}
import fs from 'fs'
import type { InvokeRequest, InvokeResponse } from 'invoke'
export default async function handler(req: InvokeRequest, res: InvokeResponse) {
// ✅ Works — file exists for this invocation only
fs.writeFileSync('/tmp/data.txt', 'content')
const content = fs.readFileSync('/tmp/data.txt', 'utf8')
// ✅ For persistent data, use KV store
await kv.set('data', 'content')
}
using Invoke;
using System.IO;
using System.Text.Json.Nodes;
[EntryPoint]
public static async Task Handler(InvokeRequest req, InvokeResponse res)
{
// ✅ Works — file exists for this invocation only
File.WriteAllText("/tmp/data.txt", "content");
var content = File.ReadAllText("/tmp/data.txt");
// ✅ For persistent data, use KV store
var kv = new KeyValueStore();
await kv.Set("data", "content");
res.Json(new JsonObject { ["content"] = content });
}
Network Restrictions
No Direct Server Binding
Functions cannot create server sockets or bind to ports because it is in sandboxed network.
Not Available:
- JavaScript
- TypeScript
- C#
// ❌ Cannot create HTTP server
const server = http.createServer()
server.listen(3000)
// ❌ Cannot create TCP server
const server = net.createServer()
server.listen(8080)
// ❌ Cannot create HTTP server
const server = http.createServer()
server.listen(3000)
// ❌ Cannot create TCP server
const server = net.createServer()
server.listen(8080)
// ❌ Cannot create HTTP server
var listener = new System.Net.HttpListener();
listener.Start(); // Not allowed in sandbox
// ❌ Cannot bind TCP sockets
var server = new System.Net.Sockets.TcpListener(8080);
server.Start(); // Not allowed in sandbox
Available:
- JavaScript
- TypeScript
- C#
// ✅ Make outbound HTTP requests
const response = await fetch('https://api.example.com/data')
// ✅ Make outbound HTTP requests
const response = await fetch('https://api.example.com/data')
// ✅ Make outbound HTTP requests
using var http = new HttpClient();
var response = await http.GetAsync("https://api.example.com/data");
Network Policy Enforcement
Outbound connections are governed by network policies configured in the admin panel.
Default: All outbound connections are allowed
Can be restricted to:
- Specific domains/IPs
- Certain ports
- Allowed protocols (HTTP, HTTPS, WebSocket)
Module Restrictions
Resource Limits
Execution Timeout
Functions have a maximum execution time.
Default: 30 seconds
Impact:
- Long-running operations will be terminated
- Use async patterns to handle multiple operations efficiently
- JavaScript
- TypeScript
- C#
// ❌ May timeout
await sleep(60000) // 60 seconds
// ✅ Design for quick responses
res.json({ status: 'processing' })
// Queue heavy work for background processing
// ❌ May timeout
await sleep(60000) // 60 seconds
// ✅ Design for quick responses
res.json({ status: 'processing' })
// Queue heavy work for background processing
// ❌ May timeout
await Task.Delay(60000); // 60 seconds
// ✅ Design for quick responses
res.Json(new JsonObject { ["status"] = "processing" });
// Queue heavy work for background processing
Memory Limits
Each function execution has limited memory.
Default: 256 MB
Impact:
- Keep data structures lean
- Stream large responses
- Avoid loading large files entirely into memory
- JavaScript
- TypeScript
- C#
// ❌ Memory intensive
const bigArray = new Array(10000000).fill('data')
// ✅ Memory efficient
const data = await kv.get('data')
res.json(data)
// ❌ Memory intensive
const bigArray = new Array(10000000).fill('data')
// ✅ Memory efficient
const data = await kv.get('data')
res.json(data)
// ❌ Memory intensive
var bigArray = Enumerable.Repeat("data", 10_000_000).ToArray();
// ✅ Memory efficient
var kv = new KeyValueStore();
var data = await kv.Get("data");
res.Json(data as JsonNode ?? new JsonObject());
CPU Limitations
Functions run in a shared environment with CPU throttling.
Impact:
- CPU-intensive operations may be slow
- Keep computations light
- Offload heavy processing to external services
Timing Restrictions
No Persistent Timers
Timers do not persist across function invocations.
- JavaScript
- TypeScript
- C#
// ❌ This won't work across invocations
setTimeout(() => {
console.log('This runs only during current invocation')
}, 5000)
// ❌ This won't work across invocations
setTimeout(() => {
console.log('This runs only during current invocation')
}, 5000)
// ❌ This won't work across invocations
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
await Task.Delay(5000, cts.Token); // Only runs during current invocation
Workaround: Use the scheduler service for recurring tasks.
Time-based Operations
Use Date.now() or new Date() for timestamps. High-resolution timing is limited.
- JavaScript
- TypeScript
- C#
// ✅ Available
const now = Date.now()
const date = new Date()
// ⚠️ Limited precision
console.time('operation')
// ... operation
console.timeEnd('operation')
// ✅ Available
const now = Date.now()
const date = new Date()
// ⚠️ Limited precision
console.time('operation')
// ... operation
console.timeEnd('operation')
// ✅ Available
var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
var date = DateTime.UtcNow;
// ⚠️ Limited precision
var sw = System.Diagnostics.Stopwatch.StartNew();
// ... operation
sw.Stop();
Console.WriteLine($"operation: {sw.ElapsedMilliseconds}ms");
Security Restrictions
Isolation
Each function runs in an isolated sandbox with no access to:
- Host file system
- Other functions' data
- Shared memory
- System resources
Environment Variables
Environment variables are read-only and configured per function version.
- JavaScript
- TypeScript
- C#
// ✅ Read environment variables
const apiKey = process.env.API_KEY
// ❌ Cannot modify
process.env.API_KEY = 'new-key' // No effect
// ✅ Read environment variables
const apiKey = process.env.API_KEY
// ❌ Cannot modify
process.env.API_KEY = 'new-key' // No effect
// ✅ Read environment variables
var apiKey = Environment.GetEnvironmentVariable("API_KEY");
// ❌ Cannot modify
Environment.SetEnvironmentVariable("API_KEY", "new-key"); // No effect
No Reflection
Limited access to sandbox internals and introspection capabilities.
Global Scope Limitations
No Global State Persistence
Global variables do not persist between invocations.
- JavaScript
- TypeScript
- C#
let counter = 0 // Reset on each invocation
export default function handler(req, res) {
counter++
res.json({ count: counter }) // Always returns 1
}
let counter = 0 // Reset on each invocation
export default function handler(req: any, res: any) {
counter++
res.json({ count: counter }) // Always returns 1
}
using System.Text.Json.Nodes;
// Static fields are reset on each invocation
static int counter = 0;
[EntryPoint]
public static Task Handler(InvokeRequest req, InvokeResponse res)
{
counter++;
res.Json(new JsonObject { ["count"] = counter }); // Always returns 1
return Task.CompletedTask;
}
Workaround:
- JavaScript
- TypeScript
- C#
// ✅ Use KV store for state
export default async function handler(req, res) {
let counter = (await kv.get('counter')) || 0
counter++
await kv.set('counter', counter)
res.json({ count: counter })
}
// ✅ Use KV store for state
export default async function handler(req: any, res: any) {
let counter = ((await kv.get('counter')) as number) || 0
counter++
await kv.set('counter', counter)
res.json({ count: counter })
}
using System.Text.Json.Nodes;
// ✅ Use KV store for state
[EntryPoint]
public static async Task Handler(InvokeRequest req, InvokeResponse res)
{
var kv = new KeyValueStore();
var raw = await kv.Get("counter");
var counter = raw is long n ? (int)n : 0;
counter++;
await kv.Set("counter", counter);
res.Json(new JsonObject { ["count"] = counter });
}
Module Caching
Modules are not cached across invocations (unlike standard Node.js).
Working with Limitations
Design Patterns
Stateless Functions:
- JavaScript
- TypeScript
- C#
// ✅ Don't rely on state
export default function handler(req, res) {
const result = processRequest(req.body)
res.json(result)
}
// ✅ Don't rely on state
export default function handler(req: any, res: any) {
const result = processRequest(req.body)
res.json(result)
}
[EntryPoint]
public static Task Handler(InvokeRequest req, InvokeResponse res)
{
// ✅ Don't rely on state
var result = ProcessRequest(req.Body);
res.Json(result);
return Task.CompletedTask;
}
External State:
- JavaScript
- TypeScript
- C#
// ✅ Use KV store for state
export default async function handler(req, res) {
const state = await kv.get('state')
const newState = updateState(state, req.body)
await kv.set('state', newState)
res.json(newState)
}
// ✅ Use KV store for state
export default async function handler(req: any, res: any) {
const state = await kv.get('state')
const newState = updateState(state, req.body)
await kv.set('state', newState)
res.json(newState)
}
using System.Text.Json.Nodes;
// ✅ Use KV store for state
[EntryPoint]
public static async Task Handler(InvokeRequest req, InvokeResponse res)
{
var kv = new KeyValueStore();
var state = await kv.Get("state") as JsonNode;
var newState = UpdateState(state, req.Body);
await kv.Set("state", newState?.ToJsonString());
res.Json(newState ?? new JsonObject());
}
API-First:
- JavaScript
- TypeScript
- C#
// ✅ Use external services for heavy work
const result = await fetch('https://api.service.com/process', {
method: 'POST',
body: JSON.stringify(req.body)
})
// ✅ Use external services for heavy work
const result = await fetch('https://api.service.com/process', {
method: 'POST',
body: JSON.stringify(req.body)
})
// ✅ Use external services for heavy work
using var http = new HttpClient();
var content = new StringContent(
System.Text.Json.JsonSerializer.Serialize(req.Body),
System.Text.Encoding.UTF8,
"application/json"
);
var result = await http.PostAsync("https://api.service.com/process", content);
Stream Large Data:
- JavaScript
- TypeScript
- C#
// ✅ Stream responses
res.type('application/json')
res.write('[')
for await (const item of getItems()) {
res.write(JSON.stringify(item) + ',')
}
res.write(']')
res.end()
// ✅ Stream responses
res.type('application/json')
res.write('[')
for await (const item of getItems()) {
res.write(JSON.stringify(item) + ',')
}
res.write(']')
res.end()
using System.Text.Json.Nodes;
// ✅ Build response as JsonArray (InvokeResponse sends a complete response)
var items = new JsonArray();
await foreach (var item in GetItemsAsync())
items.Add(item);
res.Json(items);
Next Steps
- Best Practices - Recommended patterns
- KV Store API (Bun) - State management (JavaScript/TypeScript)
- KV Store API (.NET) - State management (C#)
- Global APIs (Bun) - Available APIs for JavaScript/TypeScript
- .NET API Reference - Available APIs for C#