Common Pitfalls to Avoid When Building OpenAI Plugins: A Complete Guide
Always validate your manifest structure, await every asynchronous call, return all mutated node IDs, and handle rotating OAuth tokens to prevent silent failures and runtime errors in OpenAI plugins.
OpenAI plugins are built as Codex-plugin bundles within the openai/plugins repository, requiring strict adherence to manifest validation, skill prerequisites, and runtime constraints. Each plugin lives under plugins/<name>/ and contains a manifest (.codex-plugin/plugin.json), optional skills, agents, and commands. Understanding the architectural traps documented across the Figma, Zoom, and Base44 implementations prevents hard-to-debug failures.
Manifest and Entry Point Validation
The Codex runtime validates the plugin manifest strictly. A missing file or incorrect path stops the plugin from loading entirely.
Misconfigured Plugin.json Files
Forgetting the required .codex-plugin/plugin.json or mis-naming the skills/ folder in the manifest will prevent the plugin from loading. The Codex runtime expects the manifest exactly at plugins/<name>/.codex-plugin/plugin.json.
When referencing the skills folder, always use a trailing slash: "skills": "./skills/". The Zoom plugin provides a correct example in its manifest at plugins/zoom/.codex-plugin/plugin.json.
Skill Execution and State Management
Skills are defined in markdown files (typically SKILL.md) that document public APIs, prerequisites, and gotchas. The runtime executes skill code inside a sandbox where state is persisted only through explicit returns.
Missing Skill Prerequisites
Calling a skill like use_figma without first loading its prerequisite skill figma-use causes silent failures. The figma-use skill depends on global setup for API typings and page-state handling. Always load the prerequisite skill first, as documented in plugins/figma/skills/figma-use/SKILL.md.
Async Handling Errors
Omitting await on Promise-returning calls (e.g., figma.loadFontAsync, figma.setCurrentPageAsync) produces race conditions. The helper scripts run in an async wrapper; un-awaited Promises fire-and-forget, causing missing changes. The skill documentation explicitly requires you to “await every Promise” as noted in the figma-use rules.
Node-ID Management Failures
Not returning created or mutated node IDs from use_figma breaks subsequent calls. The Codex harness only receives data that a skill returns; without IDs, later calls cannot reference newly created objects. Always return the specific structure:
{
"createdNodeIds": [...],
"mutatedNodeIds": [...]
}
This requirement is documented in plugins/figma/skills/figma-use/SKILL.md under section 1.5.
Page Context Violations
Switching pages multiple times inside a single use_figma script causes unnecessary reloads and triggers the error “Setting figma.currentPage is not supported”. Each script reloads the page once. Call await figma.setCurrentPageAsync(page) once per script and split multi-page work into separate parallel use_figma calls.
OAuth and API Integration Pitfalls
OAuth flows and REST API interactions present specific failure modes around token lifecycle and pagination.
Token Lifecycle Assumptions
Assuming refresh tokens remain stable causes authentication failures. Some providers, including Zoom and Google, rotate refresh tokens on each use, invalidating the previous token. Implement token rotation handling as described in plugins/zoom/skills/oauth/references/full-guide.md section 1.8.
import requests, os
TOKEN_URL = "https://zoom.us/oauth/token"
CLIENT_ID = os.getenv("ZOOM_CLIENT_ID")
CLIENT_SECRET = os.getenv("ZOOM_CLIENT_SECRET")
REFRESH_TOKEN = os.getenv("ZOOM_REFRESH_TOKEN")
def refresh_access():
resp = requests.post(
TOKEN_URL,
auth=(CLIENT_ID, CLIENT_SECRET),
data={"grant_type": "refresh_token", "refresh_token": REFRESH_TOKEN},
)
data = resp.json()
# Zoom returns a new refresh token each time
os.environ["ZOOM_REFRESH_TOKEN"] = data["refresh_token"]
return data["access_token"]
Redirect URI Mismatches
Deploying a plugin with a callback URL that differs from the one registered in the provider console causes OAuth flows to reject the request before any token exchange occurs. Verify the exact URI in the provider's developer console and keep it in sync with /.app.json. See plugins/zoom/skills/oauth/troubleshooting/redirect-uri-issues.md for debugging steps.
Rate Limits and Pagination
Ignoring pagination tokens or exceeding API rate limits results in incomplete data or throttled requests. Many services return partial results and enforce limits per minute. Check the next_page_token in responses and respect Retry-After headers when receiving 429 status codes.
import time, requests, os
def fetch_all(endpoint):
token = os.getenv("ZOOM_ACCESS_TOKEN")
url = f"https://api.zoom.us/v2/{endpoint}"
results = []
while url:
resp = requests.get(url, headers={"Authorization": f"Bearer {token}"})
if resp.status_code == 429:
retry = int(resp.headers.get("Retry-After", "1"))
time.sleep(retry)
continue
data = resp.json()
results.extend(data.get("users", []))
next_token = data.get("next_page_token")
url = f"https://api.zoom.us/v2/{endpoint}?next_page_token={next_token}" if next_token else None
return results
This pattern follows the guidance in plugins/zoom/skills/rest-api/troubleshooting/common-issues.md.
Runtime and Environment Constraints
Plugins execute across multiple runtimes (Python helpers, JavaScript SDKs, OAuth flows), requiring careful handling of file systems and platform capabilities.
File System Side Effects
Creating folders or files without checking existence causes crashes on first run or in CI environments where directories already exist. Guard against existing paths or use os.makedirs(..., exist_ok=True) in Python helpers. The base44-cli skill documents this in plugins/base44/skills/base44-cli/SKILL.md.
Platform-Specific API Usage
Using APIs available only in one environment (e.g., figma.createPage() in FigJam) triggers "not supported" errors. Detect the editor type using figma.editorType and call only supported methods. The figma-use skill documentation lists design-only versus Slides-only APIs in plugins/figma/skills/figma-use/SKILL.md.
Environment Variable and Dependency Issues
Hard-coding API keys or forgetting to add them to a .env file causes runtime errors, as secrets are stripped from the repository. Create a .env placeholder (API_KEY=) and document it in the plugin's README. Additionally, importing deprecated sub-modules (e.g., @wix/data instead of @wix/wix-data-items-sdk) causes build failures. Follow upgrade notes in skill documentation, such as those in plugins/wix/skills/wix-headless/references/astro/cms/CMS_FOUNDATIONS.md.
Summary
- Validate manifest structure: Keep
.codex-plugin/plugin.jsonat the root and referenceskills/with a trailing slash. - Await every async call: Un-awaited Promises in JavaScript skills create race conditions and missing state changes.
- Return all node IDs: Always include
createdNodeIdsandmutatedNodeIdsin return values for Figma skills. - Handle OAuth rotation: Store new refresh tokens immediately, as providers like Zoom invalidate old ones after use.
- Respect rate limits: Check
next_page_tokenfor pagination andRetry-Afterheaders for throttling. - Guard file operations: Use
exist_ok=Trueor existence checks before creating directories. - Check platform APIs: Verify
figma.editorTypebefore calling environment-specific methods.
Frequently Asked Questions
What happens if I forget to return node IDs in a Figma skill?
The Codex harness cannot track objects created during skill execution. Without returning createdNodeIds or mutatedNodeIds in the skill response, subsequent commands cannot reference those objects, effectively making them unreachable in the conversation context. Always return these IDs as specified in plugins/figma/skills/figma-use/SKILL.md.
Why does my Zoom plugin fail after the first OAuth refresh?
Zoom rotates refresh tokens on every use, invalidating the previous token. If your code stores the original refresh token and reuses it, subsequent authentication attempts will fail. You must capture and store the new refresh_token from each token response, as shown in the Zoom OAuth reference at plugins/zoom/skills/oauth/references/full-guide.md.
How do I handle rate limiting in OpenAI plugin API calls?
Implement exponential backoff using the Retry-After header when receiving 429 status codes. For paginated endpoints, always check for next_page_token in the response and continue fetching until no token remains. This pattern is documented in plugins/zoom/skills/rest-api/troubleshooting/common-issues.md.
Can I use FigJam-specific APIs in a standard Figma design file?
No. APIs like figma.createPage() are restricted to specific editor types. You must check figma.editorType before calling environment-specific methods, or the runtime will throw a "not supported" error. Consult the editor mode documentation in plugins/figma/skills/figma-use/SKILL.md for the API compatibility matrix.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →