← Back to Blog

When the config is too weird: failing loud

·7 min read

The bundler tokenizer is not a parser, and it knows it. When a config file's shape does not match what it can safely edit, the right move is not to guess, it is to refuse, and to say so clearly. configure_bundler has a two-state return value that exists precisely for this.

The contract

def configure_bundler(client_dir: str) -> bool:
    """Returns True on success or when no known config file is present.
    Returns False when a known framework config exists but couldn't be
    edited, so the caller can surface a clear warning."""

Three outcomes collapse into one boolean. No config file and config edited fine both return True, there is nothing to warn about. Only the third case, a known framework exists but the edit failed, returns False.

What can fail

The injection functions return None when the expected structure is missing. Concretely: a vite.config.ts that never calls defineConfig, a next.config.mjs with neither export default nor a const nextConfig, or a file where the brace matcher cannot find a balanced object. All of these are legitimate ways to write a config, and none of them are safe to splice.

The caller turns False into a warning

The CLI, which runs the generator, does not swallow the boolean:

if not configure_bundler(client_dir):
    console.print(
        f"[yellow]⚠ Could not auto-configure bundler in {client_dir}, "
        "add a bundler alias '@pyrpc/types' → './__pyrpc.ts' "
        "(Vite/SvelteKit/Next.js Turbopack).[/yellow]"
    )

The hint text is specific and complete, it tells you to add "@pyrpc/types" -> "./__pyrpc.ts" so the generated runtime kinds resolve. The developer sees a yellow warning with an exact remediation, not a silent no-op and not a hard crash.

Why not throw?

A hard exception would be wrong here. The aliasing is an optimization of a safety net, if it is not injected, the throwing placeholder still protects the app by failing loudly at runtime. Warnings are the right severity for "I could not finish the convenience step"; exceptions are reserved for "your project is now broken". Making the failure non-fatal at codegen time pushes the decision to the developer, who may prefer to configure the alias their own way.

The layered failure design

pyrpc's failure handling is a stack, each layer louder than the last:

  • Config edited successfully → no output at all.
  • Config uneditable → yellow warning with the exact fix.
  • Warning ignored, alias missing → the Proxy throws with a full diagnosis.

The warning is the middle layer: enough signal to fix it proactively, gentle enough not to block work, and backed by a runtime guard that escalates if ignored. That is what "fail loud" means in practice, a calibrated response, not a tantrum.