Engineering defects and recovery
AI Agent Implementation Mistakes to Avoid
Identify agent defects at retry, approval, concurrency and version boundaries, with a worked publication example and proposed regression evidence.
An agent implementation can look correct while its state is wrong. The answer says a bulletin was published, but the destination rejected it. A reviewer approved an earlier draft, but the system released a later one. A retry created another publication because each attempt looked like a new request. These failures need specific engineering repairs and regression cases.
This guide is for technical leads, developers and delivery reviewers checking how an agent behaves when execution is interrupted, repeated or changed. The worked example is an illustrative internal knowledge-bulletin assistant: it prepares an update from approved material, submits the exact proposal for review and can publish only through a controlled command. The defects and tests below are proposed review material, not results from an Open for AI deployment.
Specify the state that must remain true
Before inspecting prompts, write the invariants: rules that must hold across every permitted execution path. For the bulletin assistant, a publication must refer to the approved proposal, its destination must be authorised, and a confirmed success must have a destination receipt. Repeating the same operation must not create another intended publication. An unresolved outcome must remain visible until someone or something reconciles it.
These statements make failures observable. “The agent should be reliable” cannot tell a reviewer whether an interrupted run is safe to resume. A concrete invariant can. The wider agent architecture should locate which component enforces each one; a model instruction alone is insufficient evidence.
Mistake: reporting completion from the generated answer
A model may write “published” after requesting a publication, even when the command times out. Separate the proposed action, its attempted execution and the confirmed business result. Only the destination response or an authoritative reconciliation read can establish that the intended publication exists. A successful network exchange may itself be insufficient if the destination has only accepted a background job.
The repair is an explicit state contract: prepared, awaiting approval, authorised, submitted, confirmed, rejected or outcome unknown. Do not collapse the last state into failure or success. The user-facing message should reflect the known state and explain who will resolve uncertainty. In a proposed test, the destination accepts the command but withholds the completion receipt; the assistant must report pending or unknown status and must not invent a published link.
Mistake: generating a new operation identity on every retry
A retry is another attempt to complete the same intent. Assigning it a fresh business-operation identifier can turn recovery into duplication. Conversely, treating every identical text payload as the same intent can suppress a legitimate later publication. Identify the business operation separately from its attempts and from the content it carries.
The AWS Builders’ Library discussion of idempotent APIs explains the use of caller-supplied request identifiers to distinguish repeated requests. The protection still depends on the receiving service’s contract.
For this example, retain a stable operation identifier with the approved proposal and destination. Each attempt has a separate trace identifier. Check how the destination handles the same operation with changed content, how long it remembers completed operations and what happens to delayed requests. A local “already sent” flag alone leaves a gap if the process stops after publication but before saving that flag. Where reliable deduplication or reconciliation is unavailable, prevent automatic retry of an uncertain write and route it for controlled investigation.
Mistake: resuming a paused run as though nothing repeats
Persistence does not mean that every external effect is recorded atomically with workflow state. Inspect the runtime’s actual resume semantics. LangGraph’s interrupt documentation, for example, states that a resumed node starts again from its beginning, so code before its interrupt runs again.
Suppose the assistant creates a draft record and then pauses for approval inside the same restartable unit. Resumption may create the record again unless that operation is protected. Move consequential effects behind the appropriate approval boundary, isolate them where necessary and make repeatable units safe to repeat. Persist a stable reference to a prepared object rather than reconstructing a different one silently.
Test interruption before the command, after destination acceptance and before local confirmation. Those positions expose different defects. The full orchestration design should define recovery paths; this review checks that the implementation follows them.
Mistake: attaching approval to a conversation instead of a proposal
“Approved” is incomplete without an object. Bind the decision to the proposal version, relevant content, destination and permitted action. If the draft changes materially after approval, the old approval must not authorise the replacement. A reviewer also needs to see the same content that execution will use, including any generated attachments or transformed fields.
In the bulletin example, a reviewer approves revision 4. A later edit produces revision 5 with a different effective date. The publishing command must refuse to use the earlier approval for revision 5 and request a new decision. A test that merely checks for the presence of an approval field would miss this defect. Also verify that an expired, withdrawn or unauthorised decision cannot be replayed to restart execution.
Mistake: letting a stale worker overwrite current state
Concurrent runs create a second version problem. A slow drafting worker can return after a reviewer has accepted a newer proposal. If its response blindly replaces the current object, the system may display one version while publishing another. Similar races arise when cancellation and execution arrive close together.
Use a defined ownership rule for updates and check the version on which each transition was based. Reject or reconcile a stale update instead of silently accepting it. For a consequential command, define the point after which cancellation cannot promise prevention and must become a reconciliation or corrective-action request. The interface should not offer an unconditional “cancelled” message while a destination action may already be committed.
A useful proposed concurrency test deliberately returns the older worker last. The expected result is preserved current state and an explicit stale-update outcome, not whichever response arrived most recently.
Mistake: treating valid JSON as a valid business action
A tool call can satisfy its schema while being wrong for the task. A destination identifier can be well formed but unauthorised; a date can parse but contradict the approved source; a bulletin can include an unsupported claim. Type checks are necessary, but they do not establish permission, factual support or suitability.
Keep validation decisions distinguishable. Reject malformed arguments at the interface. Check business constraints against authoritative records. Enforce permissions outside generated text. Apply the relevant agent guardrails before an action crosses its boundary. For the publication assistant, test a syntactically valid request to the wrong audience and a correctly targeted request containing an unapproved change. They fail for different reasons and should produce different repair paths.
Mistake: sending every failure back into the same loop
A temporary read failure, an invalid field, a denied permission and an uncertain write do not justify the same response. Repeating a forbidden request does not make it authorised. Rewriting arguments to satisfy an error can alter the user’s intent. Retrying an unknown write can duplicate its effect.
Map error classes to permitted transitions: bounded retry for an eligible transient fault; correction or clarification for invalid input; a stop or authorised handoff for denied access; reconciliation for an uncertain effect. Set a total execution budget as well as local retry limits, because several individually bounded loops can still produce an unacceptable run. The terminal state must preserve useful context for the receiving person.
Mistake: changing behaviour without changing the release evidence
Record the configuration that produced a result: model selection, prompt and tool contracts, retrieval configuration, source versions where available and policy rules. A prompt-only update can alter which action is selected. A tool change can preserve its name while changing what a successful response means. Neither should disappear inside an unchanged release label.
Choose whether in-flight runs remain on their original configuration, migrate through a documented transformation or stop for review. Define how a rollback affects already completed business actions; restoring an earlier application version does not retract a published bulletin. Keep representative regression cases connected to the changed contracts in the testing and evaluation plan.
| Injected condition | Required observation | Evidence to inspect |
|---|---|---|
| The destination publishes but the response is lost | Recovery finds the existing result without creating another publication | Operation identity, destination record and subsequent attempts |
| Revision 4 is approved; revision 5 is submitted | The changed proposal requires a new approval | Approved object, submitted object and rejection reason |
| An old worker finishes after the current draft is accepted | The stale update cannot replace the accepted version | Expected and current state versions |
| A run resumes after a tool-contract change | The declared compatibility or migration rule is applied | Run configuration and selected continuation path |
| Permission is denied repeatedly | The run reaches its defined terminal or handoff state | Error category, attempt history and final owner |
Make the repair observable after release
A regression test proves only what its setup and assertions cover. Preserve the repaired failure as a reusable case, inspect the destination effect and verify the user-visible status. If the test uses a substitute service, state which real-service behaviours remain unverified. Do not infer production resilience from a mocked success response.
Connect each repaired invariant to a focused operational signal: unresolved writes awaiting reconciliation, stale updates, rejected approval versions or exhausted run budgets. Alert an owner who can act; collecting more text without a response path does not close the defect.
For a scoped AI agent development project, review the command contract, state transitions and recovery cases alongside the useful happy path. Request an implementation review discussion with a redacted failing trace, the expected destination state and the recovery behaviour you need. Those materials make a precise repair scope possible.