Troubleshooting
The add-in won't load / "This add-in is not supported"
- Confirm you're using Microsoft 365 (current channel). The manifest requires
SharedRuntime 1.1; host-specific API sets (WordApi 1.3,ExcelApi 1.1,PowerPointApi 1.4) are checked at runtime. Perpetual Office 2016/2019/2021 won't satisfy these. - Make sure the dev server is running on HTTPS (
https://localhost:3000) and the certificate files exist. Office blocks HTTP and untrusted certs. - Re-upload the manifest if you edited
manifest.xml.
Connection test fails
- Key rejected (401/403): wrong or expired API key. Regenerate it.
- 404: wrong endpoint path. Endpoints are base URLs (no trailing slash), e.g.
https://opencode.ai/zen/go/v1, not/v1/chat/completions. - CORS error: the provider doesn't allow browser-origin requests. On a self-hosted deployment, enable Advanced → Proxy API requests through this server to forward requests server-to-server. On a hosted instance, use a CORS-friendly provider (OpenAI, Gemini, Anthropic, OpenRouter), see Compatibility.
- Anthropic CORS: the add-in sends the
anthropic-dangerous-direct-browser-accessheader automatically; if you're calling Anthropic through a proxy that strips headers, add it there. - Ollama unreachable: confirm
ollama serveis running and the model is pulled. The endpoint must match whereollama serveis listening (localhostfor a local install, or the server address for a shared Ollama).
When to use the old /chat/completions endpoint
Some OpenAI-compatible endpoints (and older Ollama versions) don't implement the Responses API (/responses). If tool calls or streaming misbehave, open Settings → Advanced and enable Use old /chat/completions endpoint.
Reasoning block never appears
Reasoning depends on the model, not the add-in:
- The model must actually emit reasoning tokens (e.g. a DeepSeek-style model via OpenCode, Claude thinking, Gemini thought parts).
- Non-reasoning models produce no block, and that's expected.
If reasoning used to work and stopped, check you're on the latest build; the OpenCode stream uses response.reasoning_text.delta, which is handled explicitly.
The model returns an empty response
The add-in shows a visible note when a reply comes back empty with no tool call. Causes and fixes:
- Output token limit: the reply was cut off. Raise Max Tokens in Settings (Advanced).
- Provider stall / malformed request: retry, or run Test Connection.
A truncated reply (content but cut off) shows a specific truncation notice instead.
"PowerPoint assigned new ids" warning
OOXML write-backs re-import the slide, which makes PowerPoint renumber the slide and all its shapes. After any edit_slide_text / edit_slide_xml, re-run list_slide_shapes to get fresh shape ids before more edits. This is expected behavior, not a bug.
Shapes silently dropped after an OOXML edit
PowerPoint can drop malformed XML during re-import. The tool verifies the re-imported shape count and warns when shapes were lost. Prefer insert_slide_element for adding shapes; use edit_slide_xml only for text/table-level edits you can't do otherwise, and verify with read_slide afterwards.
A background shape covers my slide text
Symptom: after changing the theme, text is hidden and you only see the background color. Cause: a full-bleed rectangle is stacked on top of the text in the slide's shape order, hiding it. Two ways this is handled:
- New backgrounds:
insert_slide_elementautomatically places full-bleed shapes (covering nearly the whole slide) at the back of the stacking order, behind existing content. Insert the rectangle with full slide size, then fill it withformat_shape. - Existing slides: run
verify_slides; it reportsz_order_warningswhen a background is stacked in front of content. Re-insert the background as a full-bleed shape so it sits behind the text.
Note: PowerPoint.js has no z-order API, so scripts that try shape.zOrder(...) fail with "is not a function". Use insert_slide_element (full-bleed shapes auto-back) or list_slide_shapes (order field, 0 = back) to inspect stacking.
"Cannot read properties of null (reading 'async')" in edit_slide_xml
The tool exposes a slidePath global; the zip always contains exactly one slide at that path. Never guess position-based names like slide2.xml; they don't exist. If you still see the error, use zip.file(slidePath).
Office.js property-not-loaded errors in execute_office_js
Office.js proxies only expose loaded properties. Before reading, load and sync:
js
const shapes = slide.shapes;
shapes.load("top,left,width,height,name");
await context.sync();
const items = shapes.items;The add-in appends this hint to the error automatically when it detects a property-not-loaded failure.
The conversation looks wrong or I want a clean slate
- Clear conversation: trash icon in the header.
- Reset settings: clear browser storage for the add-in's origin, or overwrite fields and press Apply.
SSO: "redirect_uri_mismatch" from the identity provider
The redirect URI the server sends must match, character for character, the one registered at your provider. Behind a reverse proxy the computed value can be wrong, so set it explicitly:
dotenv
OPENDOCBOT_OIDC_REDIRECT_URI=https://<your-host>/auth/callbackThen restart and try again. See SSO.
SSO: "group overage" / access denied
When OPENDOCBOT_OIDC_ALLOWED_GROUPS is set and the user belongs to too many groups for the token to list them, the provider sends a group-overage claim and sign-in is refused. Reduce the user's group membership, or use app roles. The group values are object IDs, not display names.
SSO: users are signed out after a restart
OPENDOCBOT_SESSION_STORE must point at a path that survives restarts (in Docker, the /data volume). If it is not writable, the server logs a warning and sessions are lost. Check the store path and its permissions.
Managed config: the refresh button shows an error
The server could not be reached, so the add-in kept the last configuration it loaded. Use the refresh button (next to the settings icon) to retry. If no configuration ever loaded, configure a provider manually or import a configuration export.
Still stuck?
Use debug export (copy button in the chat); it captures the full conversation, tool traces, and debug log. Paste it into a GitHub issue with a description of what you did.