Skip to content

Commit e514ce6

Browse files
fix(provider-core): Codex turns refused by a managed access policy say so instead of 'retry'
Codex rejects turn/start when an org-managed requirements.toml or cloud policy forbids the thread's approval policy or sandbox mode, and the failure showed only the generic turn-start message telling the user to retry. Recognize Codex's fixed-shape constraint rejection for approval_policy and sandbox_mode and name the managed requirement instead. Fixes #25674 AI-assisted; verified with the tests and tools listed above.
1 parent 737db91 commit e514ce6

1 file changed

Lines changed: 14 additions & 2 deletions

File tree

‎packages/provider-core/src/server/failure.ts‎

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ const PROVIDER_FAILURE_CATEGORIES: Partial<
6565
const CODEX_GOAL_VALIDATION_MESSAGE =
6666
/^goal objective must (?:not be empty|be at most \d{1,6} characters)$/u;
6767

68+
// codex_config::ConstraintError::InvalidValue for a managed requirements.toml or cloud policy.
69+
const CODEX_ACCESS_REQUIREMENT_REJECTION =
70+
/invalid value for `(approval_policy|sandbox_mode)`: `([A-Za-z]{1,32})` is not in the allowed set /u;
71+
6872
const PROVIDER_FAILURE_REASONS: Record<ProviderAdapterFailureReason, string> = {
6973
previous_turn_active: "the previous turn is still running",
7074
attachment_unreadable: "an attachment could not be read",
@@ -119,8 +123,8 @@ function curatedCauseMessages(cause: unknown): {
119123
case "ProviderCompactionUnsupportedError":
120124
return { known: stringField(cause, "message"), category: categoryMessage() };
121125
case "CodexAppServerRequestError": {
122-
// Only Codex's fixed-shape goal validation text is actionable and
123-
// safe to show; other request error text stays hidden.
126+
// Only Codex's fixed-shape goal validation and managed-requirements
127+
// text is actionable and safe to show; other request error text stays hidden.
124128
const rejection = stringField(cause, "message");
125129
if (
126130
(cause as Record<string, unknown>).method === "thread/goal/set" &&
@@ -129,6 +133,14 @@ function curatedCauseMessages(cause: unknown): {
129133
) {
130134
return { known: `Codex rejected the goal: ${rejection}.`, category: categoryMessage() };
131135
}
136+
const access =
137+
rejection === undefined ? null : CODEX_ACCESS_REQUIREMENT_REJECTION.exec(rejection);
138+
if (access !== null) {
139+
return {
140+
known: `Codex's managed requirements do not allow \`${access[1]}\` \`${access[2]}\`, which this thread's access mode uses. Switch the thread to an access mode the policy allows.`,
141+
category: categoryMessage(),
142+
};
143+
}
132144
break;
133145
}
134146
case "AcpProcessExitedError": {

0 commit comments

Comments
 (0)