Skip to content

Commit 5a86b02

Browse files
committed
fix(logs): keep one logged mark per execution and attribute unparseable responses
A raw value logged at an execution boundary now remembers every execution that logged it (bounded), so overlapping runs sharing one persistent rejection each log it once. A 2xx body that is not JSON is the endpoint's failure, not Sim's.
1 parent eb8e73a commit 5a86b02

3 files changed

Lines changed: 30 additions & 5 deletions

File tree

‎apps/sim/lib/core/errors/failure-log.test.ts‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,13 @@ describe('logFailureOnce', () => {
111111
expect(outerBoundary(persistentFault)).toBe('internal')
112112
expect(outerBoundary(persistentFault, '')).toBe('internal')
113113
})
114+
115+
it('logs one persistent value once in each of two overlapping executions', () => {
116+
const persistentFault = new Error('module failed to load')
117+
expect(outerBoundary(persistentFault, 'exec-a')).toBe('internal')
118+
expect(outerBoundary(persistentFault, 'exec-b')).toBe('internal')
119+
120+
expect(outerBoundary(persistentFault, 'exec-a')).toBeUndefined()
121+
expect(outerBoundary(persistentFault, 'exec-b')).toBeUndefined()
122+
})
114123
})

‎apps/sim/lib/core/errors/failure-log.ts‎

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,15 @@ const MAX_CAUSE_DEPTH = 8
3030
* tool failure's output, a block error, a handler's rebuilt error), never a raw thrown value: a
3131
* persistent fault can rethrow one object forever (a rejected dynamic `import()`, a memoized
3232
* rejected promise), and marking it would silence every later occurrence process-wide.
33-
* `loggedInExecution` scopes a raw value's mark to the one execution that logged it.
33+
* `loggedInExecution` scopes a raw value's mark to the executions that logged it, so concurrent
34+
* runs sharing one persistent rejection each log it once.
3435
*/
3536
const failureKinds = new WeakMap<object, FailureKind>()
3637
const loggedCarriers = new WeakSet<object>()
37-
const loggedInExecution = new WeakMap<object, string>()
38+
const loggedInExecution = new WeakMap<object, Set<string>>()
39+
40+
/** Bounds the executions remembered per raw value, which a persistent fault shares across runs. */
41+
const MAX_EXECUTIONS_PER_VALUE = 32
3842

3943
function isKeyable(value: unknown): value is object {
4044
return (typeof value === 'object' || typeof value === 'function') && value !== null
@@ -126,7 +130,7 @@ function wasFailureLogged(error: unknown, executionId?: string): boolean {
126130
return causeChain(error).some(
127131
(link) =>
128132
loggedCarriers.has(link) ||
129-
(Boolean(executionId) && loggedInExecution.get(link) === executionId)
133+
(executionId !== undefined && loggedInExecution.get(link)?.has(executionId) === true)
130134
)
131135
}
132136

@@ -167,6 +171,14 @@ export function logFailureOnce(
167171
failureKind,
168172
})
169173
/** An empty id (a request that failed before minting one) scopes nothing. */
170-
if (executionId && isKeyable(error)) loggedInExecution.set(error, executionId)
174+
if (executionId && isKeyable(error)) {
175+
const executions = loggedInExecution.get(error) ?? new Set<string>()
176+
if (executions.size >= MAX_EXECUTIONS_PER_VALUE) {
177+
const oldest = executions.values().next().value
178+
if (oldest !== undefined) executions.delete(oldest)
179+
}
180+
executions.add(executionId)
181+
loggedInExecution.set(error, executions)
182+
}
171183
return failureKind
172184
}

‎apps/sim/tools/index.ts‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3059,7 +3059,11 @@ async function executeToolRequest(
30593059
try {
30603060
responseData = await response.json()
30613061
} catch (jsonError) {
3062-
throw new Error(`Failed to parse response from ${toolId}: ${jsonError}`)
3062+
/** The endpoint answered with a body that is not JSON; not Sim's fault. */
3063+
throw markFailureKind(
3064+
new Error(`Failed to parse response from ${toolId}: ${jsonError}`),
3065+
'third_party_server'
3066+
)
30633067
}
30643068
}
30653069
}

0 commit comments

Comments
 (0)