Error messages with Droid Exec

Rewrite error messages across services so they are consistent, descriptive, and actionable, using droid exec and a shared style guide.

Use this recipe to bring error messages in line with one style across many files or services. You write a short style guide, the script finds files that throw or return errors, and one droid exec run per file rewrites the message strings to match the guide. Error types, status codes, and control flow stay the same.

How it works

  1. 1
    Find candidates. git grep lists tracked files that match an error pattern. The default matches new SomethingError( and throw new Error(. Set PATTERN to match your own error helpers.
  2. 2
    Rewrite in parallel. Each file gets its own droid exec --auto low run that edits only message strings.
  3. 3
    Review. Nothing is committed. You read the diff, run your tests, and update any tests that assert on the old wording.

Prerequisites

  • The Droid CLI is installed and FACTORY_API_KEY is set. See Droid Exec installation.
  • A style guide file in the repository. The next section has a starting point.

Write a style guide

Droid follows concrete examples more reliably than general advice. Keep the guide short and include real before and after pairs from your codebase:

error-style.md
# Error message style
 
- Say what failed, then what the caller can do about it.
- Name the input or resource involved, using IDs that are already in scope.
- Never include secrets, tokens, passwords, or full request bodies.
- Use sentence case and no trailing period.
- Keep messages for internal errors short; users see a generic message.
 
Before: "Invalid input"
After:
  "Missing required field 'email'. Provide a valid email address"
 
Before: "Not found"
After:
  `Project ${projectId} not found. Check the ID and that you have access`
 
Before: "Auth error"
After:
  "Session expired. Sign in again to continue"

The script

droid-refactor-errors.sh
#!/usr/bin/env bash
# Rewrite error messages to match a style guide with droid exec.
# Usage: ./droid-refactor-errors.sh <style-guide> [pathspec ...]
set -euo pipefail
 
USAGE="usage: droid-refactor-errors.sh <style-guide> [pathspec ...]"
STYLE_GUIDE="${1:?$USAGE}"
shift
CONCURRENCY="${CONCURRENCY:-4}"
DRY_RUN="${DRY_RUN:-false}"
PATTERN="${PATTERN:-new [A-Za-z]*Error\(}"
 
# git grep exits 1 when nothing matches and 128 when it can't run,
# such as on an invalid PATTERN.
status=0
files=$(git grep -l -E "$PATTERN" -- "${@:-.}") || status=$?
if [ "$status" -gt 1 ]; then
  echo "git grep failed (exit $status). Check PATTERN." >&2
  exit "$status"
fi
if [ -z "$files" ]; then
  echo "No files match $PATTERN."
  exit 0
fi
 
echo "$files" | while read -r f; do
  echo "$(git grep -c -E "$PATTERN" -- "$f" | cut -d: -f2) matches: $f"
done
[ "$DRY_RUN" = "true" ] && exit 0
 
refactor_file() {
  local file="$1"
  if droid exec --auto low "Rewrite the error messages in $file
to follow $STYLE_GUIDE. Only change message strings. Keep error classes,
status codes, error codes, metadata, and control flow exactly as they are.
Use only variables already in scope when adding context.
Never add secrets or personal data to a message.
If a message already follows the guide, leave it unchanged." >/dev/null; then
    echo "done: $file"
  else
    echo "failed: $file" >&2
    return 1
  fi
}
export -f refactor_file
export STYLE_GUIDE
 
echo "$files" | tr '\n' '\0' | xargs -0 -P "$CONCURRENCY" -I {} \
  bash -c 'refactor_file "$1"' _ {}

Run it

Preview which files match and how many errors each has:

Bash
DRY_RUN=true ./droid-refactor-errors.sh error-style.md services/billing

Match your own error helpers with PATTERN, then apply:

Bash
PATTERN='(HttpError|AppError)\(' \
  ./droid-refactor-errors.sh error-style.md services/
The error message script listing match counts for two files, then reporting both done (sped up)

With the style guide above, Droid rewrote messages like these and left the error classes and status codes alone:

Diff
- 'Not found'
+ `Project ${projectId} not found. Check the ID and that you have access`
- 'Invalid input'
+ "Missing required field 'email'. Provide a valid email address"

Review the changes

  • Run your tests. Tests that assert on exact message text fail by design; update them to the new wording in the same pull request.
  • Check that no message now includes secrets or personal data. Droid is told not to add them, but review the diff before you merge.
  • Ask client teams before you change messages that API consumers parse. Prefer stable error codes for anything machines read.

To keep messages consistent after the refactor, reference the style guide from your AGENTS.md so Droid follows it in everyday work too. See AGENTS.md.