Troubleshooting and FAQ
Answers to the problems agents actually hit, in the shape you hit them. Every fix on this page is the same loop worn into different grooves: read current state, correct the call, run once, verify.
Why did my whole run fail even though I caught the error?
Check what you caught. A provider rejection that you catch does not fail the run: the run settles with your result, and the call stays visible in calls as failed_but_handled, so a partial result cannot pass as a complete one. Two things a catch cannot save: exceeding the 64 provider calls is enforced outside your code, and a rejection that your handler rethrows still ends the run. Both now return a failure envelope with the public error message, bounded logs, and available ledger entries. Do not catch an unexpected error only to make it visible; let it fail visibly. If you catch an expected error so work can continue, return its message in the relevant item status.
Some misses are answers rather than failures, and those do not throw at all. documents.getDocument returns a normal value with an error discriminant when the miss is correctable:
{ error: "block_not_found", requestedBlockKey, availableBlockKeys }with a sample of real keys to retry with.{ error: "section_anchor_not_heading", ... }when a section scope is anchored on a block that is not a heading, listing the document's real heading keys.{ error: "version_not_found", requestedVersion, currentVersion, nearestSurviving }with the nearest surviving versions on either side.
Branch on the shape instead of reaching for try and catch:
const read = await documents.getDocument({
documentId: "0199c3a1-6f0e-7b2d-9c11-4a1c2d3e4f50",
scope: { type: "block", blockKey: "b7f2a9" },
})
if (read.error === "block_not_found") return { retryWith: read.availableBlockKeys.slice(0, 5) }
return read.scope.blockKeysOne exception works in your favor: calling a method name that does not exist fails with the namespace's whole callable surface in the message, so a single wrong guess teaches you every right signature.
Is this an authorization failure, or does the thing not exist?
The messages tell you which correction to make, not which secret to infer.
An organization-scoped call with an id you do not hold an active membership in fails with Organization not found among your active memberships. Call organizations.listMine and choose one of those organizationId values. The fix is exactly that: page through organizations.listMine and use one of the returned ids. Wrong id and missing membership read the same on purpose.
A documents.getDocument call that cannot produce a document fails with Document not found. Either the id is wrong or the document is not visible to you. Check the id first, then confirm you belong to the organization that owns it.
Seeing a namespace described by guide is not an authorization grant. Every call inside run is checked again when it runs, against your delegated scopes, your live organization access, and the connection's state. A read-only grant can read everything its declarations expose and still have every mutation rejected.
Why was my edit rejected with stale_live_revision?
Because the document changed between your read and your write, and the platform refused to clobber the newer state. documents.editDocument, documents.patchSettings, and documents.patchDocumentEnvelope are guarded by optimistic concurrency: read the document live, then pass the returned writeBasis back unchanged.
Document changed since your last live read (basis liveRevision 41, current liveRevision 43).
Call getDocument again, inspect the latest state, then retry with the returned writeBasis.Do exactly what the message says. Re-read, look at what changed, decide whether your edit still makes sense, and only then write with the fresh writeBasis. A malformed or invented token fails with invalid_write_basis instead; the cure is the same fresh read. Historical reads, meaning getDocument with a version, never return a writeBasis, so you cannot accidentally write against a snapshot.
Be most careful here after an ambiguous failure. Resending the same editDocument call blind can apply the same edit twice if the first attempt landed. One call is one atomic batch and one undo step: it applied entirely or not at all, so a single read tells you which world you are in.
Why does search return nothing when I know the document exists?
First, know your four modes. search accepts mode: "hybrid" (the default, keyword and meaning fused), "semantic" (meaning only, for when the corpus does not use your wording), "fts" (keyword only, for names, identifiers, and rare terms), and "exact" (deterministic substring matching, case-insensitive; there is no regular expression mode).
Now the sharp edge: the ranked modes, hybrid, semantic, and fts, run against an index that mirrors your documents and lags writes by a few minutes, typically 3 to 5. A document created seconds ago is usually invisible to them. Two reads never lag: listDocuments, which queries canonical storage directly, and search with mode: "exact", which matches against canonical stored text. Verify a write with getDocument, never with search.
Every mode, exact included, reads the last persisted version of a document rather than unsaved live edits, so getDocument can show text that no search can find yet. And archived documents are excluded from every read by default; pass archived: "include" to see them.
An empty results array is the only miss signal there is. Before concluding something does not exist, work down this ladder:
Rephrase the query, or switch mode:
ftsfor a rare term,semanticfor a concept,exactfor a literal string.Widen the date filters, or drop
typeIds.Check
truncated. Outsidesemanticmode,truncated: truemeans matching documents may sit past what came back, so narrow filters or raiselimit.Fall back to
listDocuments, which cannot lag and whose filters always apply exactly.Last rung:
archived: "include".
A call returned a truncated envelope instead of my result. Can I use it?
No. When a provider result exceeds the per-result size limit, the call has already run, and the runtime hands you a placeholder instead of killing the run: an envelope with codemodeResultTruncated: true, the tool name, originalSizeBytes, maxSizeBytes, a preview, and guidance. The preview is for inspecting shape only. Do not read fields off it, and do not compute with it. Re-issue the call with a narrower ask: a tighter scope, a smaller limit, fewer fields returned from your script.
Oversized arguments are the opposite case: they fail before the provider is called, nothing was attempted, and a corrected retry is plainly safe.
How do I retry a create without making a duplicate?
Give documents.createDocument a caller-owned creationId used only as an idempotency key. It must be a UUIDv7 (time-ordered); a UUIDv4 is rejected before the create runs. Generate it on your host when you can, or mint one with async () => await ids.mintOperationIds({ count: 1 }) in a separate preparation run (the helper must be the first and only provider call), preserve the returned UUID outside the sandbox, and paste it as a literal into the create run. Reuse it only when retrying that same intended document.
return await documents.createDocument({
organizationId: "0199aa02-1c44-7c31-88ef-9d2b4c6a8e10",
typeId: "0199b7de-3f21-7a90-8c44-2e5f7a1b3c6d",
creationId: "0199b7e0-4c2a-7d31-8a5e-3f6b9c2d1e40",
title: "Q3 retrospective",
draft: { markdoc: "# Q3 retrospective\n\nWhat we learned.\n" },
})A retry with the same creationId returns the existing document instead of creating a second one, and the response's action field says which happened: "created" the first time, "alreadyBootstrapped" on a replay. The replay ignores the retry's draft. One guard to respect: a replay that contradicts the original, a different title, type, or metadata, fails rather than reconciling. Never reuse a creationId across documents.
editDocument has no idempotency key. For edits, the safe retry is the writeBasis loop above: read, inspect, decide, write once.
My run timed out. What actually happened to my writes?
A run gets 30 seconds of awaited work. The timeout does not roll anything back: work that already reached the product stays there. A sandbox timeout now returns Execution timed out in error, plus bounded logs and the available calls ledger. The host fallback, Codemode executor timed out after 30000 ms, is used only when the sandbox does not answer and may carry less detail. Keep runs small enough to finish, and read current state before retrying any write whose outcome is ambiguous.
On the runs where you do get a ledger, read it the way the runtime means it. succeeded means the call returned, which is a lower bound on what reached the product, not proof a write persisted. failed_but_handled means the call rejected and your code carried on. in_flight_unknown calls never reported an outcome at all. The ledger tells you what to verify, never what to assume: before retrying anything it names, read current state with getDocument or the relevant receipt.
An interrupted createDocument never leaves a half-made document behind. The platform deletes a shell that never finished and recovers a document that did, so the correction is always the same: retry the same call unchanged, with the same creationId, and you converge on one document.
The limits worth knowing, because splitting work across runs beats retrying into the same ceiling: 64 provider calls per run, 30 seconds, 32 KiB of code, 512 KiB of arguments per call, normally 128 KiB per provider result with tool-specific exceptions, 128 KiB for the final result, and capped logs.
Why did an integration call fail with a bare name like ConnectorNotFoundError?
The source id you passed does not resolve to a usable connection. Integration sources are per connection, and a reconnect supersedes the old connection and mints a new sourceId, so cached ids go stale. This is an ordinary rejected provider call. If it is uncaught, the failure envelope exposes the exact message. Catch it only when the workflow can recover or continue; otherwise let the run fail visibly.
The recovery is cheap: call the namespace's listSources at the start of a session and work from fresh ids. Each source row also carries its lifecycle: an active source is usable, needs_reauth means the connection needs renewed consent from its owner, and a paused source refuses new provider calls until someone resumes it. Pausing is connection-scoped; it never revokes credentials or touches other connections.
How do I see a document's earlier versions?
documents.listVersions({ documentId }) lists the saved versions newest first, with when each was made, who contributed and how many blocks were added, removed or changed. Read one with documents.getDocument({ documentId, version }); the title and metadata in that answer are the current ones. documents.compareVersions({ documentId, from, to }) shows what changed between two versions, and documents.blockHistory({ documentId, blockKey }) follows one block through them. Version history is for members; a reader of a public organization gets none. No operation turns a whole document back into an earlier version: read the old text and write back the parts you need with editDocument.
How do I restore a deleted document?
documents.deleteDocument moves a document to the trash. documents.listDocuments({ organizationId, deleted: "only" }) lists the trash, newest deletion first, with deletedAt and restorableUntil. documents.restoreDocument({ documentId, from: "trash" }) brings a document back with the same id, links, version history and comments. After 30 days a restore is refused and the content is then removed for good. To take a document out of everyday retrieval without deleting it, archive it instead; archiving can always be undone.
Can a document be signed?
Yes, in the Oddlynew web app. A member who can edit a document sends one saved version of it, as a PDF, for signature, and can remind a signer or withdraw the request. The signer opens the link, signs in with the Oddlynew account of the invited email address, and signs by typing their name or declines. The signed PDF carries a signature record with the time of signing and can be downloaded. Agents cannot send signature requests yet. The activity log records signature_sent and signature_completed, and deleting a document with a completed signature needs acknowledgeSignatureEvidenceLoss: true.
How do I report illegal content?
Use the form at oddlynew.com/report; it needs no account. An agent can file the same notice with directory.reportIllegalContent, which needs a connection with write access: give the addresses of the content (up to 20), a category, your explanation, whether you report as an individual, an organization, a rights holder or an authority, and confirm that the notice is accurate and complete to the best of your knowledge. On the form, your name and email address are required, except for reports of child sexual abuse material; through an agent, pass your name explicitly except for those reports; an OAuth connection supplies the signed-in person's verified email when you omit it, while an API key must supply it, and for child sexual abuse reports both are optional and no email is filled automatically. You receive a reference number. A report hides nothing by itself: a person reviews it and tells you the decision by email. For a breach of Oddlynew's terms rather than the law, use directory.flag. How we handle the data in a report is in section 8 of the privacy notice.
How do I see what changed recently?
In your own organization, logs.changes({ organizationId, createdAfter }) lists document changes newest first for the last 90 days; it needs the activity log permission, which team roles carry, including a shared agent's Writer role; Content API and Documents API keys do not (see Organization activity log). For a catalog view, documents.listDocuments({ organizationId, updatedAfter }) lists the documents changed since a time, and documents.listVersions shows what changed inside one document. Oddlynew does not publish a list of product changes yet.
Where does the full story live?
This page is triage. The mechanics have their own pages: Errors, receipts, and safe retries for the complete retry doctrine including the cross-organization copy workflow, Search and metadata for retrieval in depth, and Documents: reading and writing for scopes and the edit grammar.