How the OpenWork Automation Runner Handles Model Access Lost Errors
The OpenWork automation runner validates model access every time a run is claimed, and if the owner no longer has permission to use the selected model, it calls skipRun to mark the automation as skipped with a structured model_access_lost error code.
The different-ai/openwork repository implements a robust automation system that prevents unauthorized model usage by verifying permissions at dispatch time. When an automation runner attempts to claim a run, it checks whether the owner still has valid membership and model access before proceeding. Understanding how the OpenWork automation runner handles model access lost errors is essential for maintaining reliable scheduling and avoiding compute waste.
Model Access Validation in the Automation Runner
Before executing any automation, the runner invokes resolveAutomationModelAccessWithStore (exported publicly as resolveAutomationModelAccess) from ee/apps/den-api/src/automations/authority.ts. This function performs a three-layer validation to ensure the automation owner retains the right to invoke the specified model.
The Three-Layer Permission Check
The authority system validates the following in sequence:
- Owner membership –
findActiveMemberverifies the owner is still an active member of the organization. If not, it returnsowner_membership_lost. - Provider availability –
findProviderorfindOpenWorkProviderconfirms the provider (OpenWork, custom, or free tier) still exists. Failure yieldsprovider_unavailable. - Model existence and permission –
findModelcombined withcanAccessProviderensures the model is present and the member retains access. If either check fails, the function returnsmodel_access_lost.
When any layer fails, the function returns an AutomationAuthorityFailure object:
{
ok: false,
code: "model_access_lost",
message: "...human-readable explanation..."
}
Handling Failed Access in the Repository Layer
When the runner receives a failure result from the authority check, the automation service immediately calls skipRun implemented in ee/apps/den-api/src/automations/repository.ts. This method durably marks the run as non-executable to prevent wasted compute and infinite retry loops.
The skipRun method updates the AutomationRun record with:
status: "skipped"error: { code: "model_access_lost", ... }result_summaryandfinished_attimestamps
This guarantees the run is permanently recorded as not executed according to the source code in repository.ts.
Complete Error Flow from Claim to Skip
The end-to-end handling follows this strict sequence:
- Claim – The runner fetches the automation definition and calls
resolveAutomationModelAccess. - Success – If the model remains accessible, the run proceeds from queued to running status.
- Failure – If resolution returns
ok: falsewithcode: "model_access_lost", the runner invokesskipRunwith the error details. - Result – The run appears in the UI as skipped with a clear error message, and the automation’s next-due time updates according to its schedule.
The test suite in ee/apps/den-api/test/automations-model-authority.test.ts explicitly verifies this behavior, including a test that ensures removed memberships cannot inherit model access and that revoked providers trigger the appropriate error codes.
Code Examples
Resolving Model Access
This snippet demonstrates how the runner checks permissions before dispatch:
import { resolveAutomationModelAccess } from '@/automations/authority'
const input = {
organizationId: 'org_123',
ownerMemberId: 'mem_456',
providerId: 'openwork',
modelId: 'z-ai/glm-5.2',
}
const result = await resolveAutomationModelAccess(input)
if (!result.ok) {
// result.code will be "model_access_lost", "owner_membership_lost", etc.
console.error(`Cannot run automation: ${result.message}`)
// Runner proceeds to call repository.skipRun(...)
}
Skipping a Run Due to Lost Access
When validation fails, the runner persists the skip state:
import { automationRepository } from '@/automations/repository'
await automationRepository.skipRun({
runId: 'run_789',
code: 'model_access_lost',
message: 'The selected model is no longer available for this owner.',
now: Date.now(),
})
Runner Dispatch Workflow
A simplified view of the dispatch logic shows the guard pattern:
async function dispatchRun(run) {
const access = await resolveAutomationModelAccess({
organizationId: run.organizationId,
ownerMemberId: run.ownerMemberId,
providerId: run.revision.model.providerId,
modelId: run.revision.model.modelId,
})
if (!access.ok) {
await automationRepository.skipRun({
runId: run.id,
code: access.code,
message: access.message,
now: Date.now(),
})
return // abort dispatch
}
// Continue with normal execution...
}
Summary
- Proactive validation – The runner checks
resolveAutomationModelAccessWithStorebefore every dispatch to verify the owner, provider, and model remain valid. - Structured error codes – Failures return specific codes like
model_access_lost,owner_membership_lost, orprovider_unavailabledefined inauthority.ts. - Durable skip mechanism – The
skipRunmethod inrepository.tsmarks runs asskippedwith full error metadata, preventing retries and wasted resources. - Test coverage –
automations-model-authority.test.tsvalidates that removed memberships and revoked providers correctly trigger access errors.
Frequently Asked Questions
What triggers a model_access_lost error in OpenWork?
The error triggers when the owner of an automation is no longer an active member of the organization, the provider has been removed, or the specific model is no longer accessible to that member. The resolveAutomationModelAccess function in authority.ts performs these checks and returns the error code before any model invocation occurs.
How does the runner prevent retries after access is lost?
Instead of throwing an unhandled exception that might trigger retry logic, the runner explicitly calls skipRun from repository.ts. This updates the database record with status: "skipped" and timestamps, signaling to the scheduler that the run was intentionally abandoned and should not be retried.
Where is the error information stored when a run is skipped?
The error details are stored in the AutomationRun table within the error column as a JSON object containing the code (e.g., "model_access_lost") and a human-readable message. The result_summary and finished_at fields are also populated to indicate completion state.
How can I test the model access validation logic?
The repository includes comprehensive tests in ee/apps/den-api/test/automations-model-authority.test.ts. These tests simulate revoked memberships, deleted providers, and removed models to ensure the resolveAutomationModelAccess function returns the expected AutomationAuthorityFailure objects and that the runner correctly skips affected automations.
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 →