Skip to content

[Bug]: auggie cloud expert apply emits null for session_config.visibility / user_metadata / auto_cleanup_on_idle, server rejects with expected i32 #128

@asilluron

Description

@asilluron

What happened?

auggie cloud expert apply -f bundle.yaml fails for every ExpertBundle I throw at it — including round-tripping a known-good auggie cloud expert export of an already-deployed expert (Advisor) — with:

Failed to apply expert bundle: HTTP error: 400 Bad Request: Json deserialize error: invalid type: null, expected i32 at line 1 column 310
  Request ID: 1fc3ed43-a53b-4773-8841-b700e6b08b2e

The column number shifts with bundle size but the error class is identical. auggie cloud expert validate and auggie cloud expert apply --dry-run both pass — the failure is only on the live POST.

I patched globalThis.fetch from NODE_OPTIONS to log outgoing bodies and the cause is clear. The CLI sends:

{
  "scope": 2,
  "config": {
    "name": "Linear Groomer Diag",
    ...
    "session_config": {
      "model": "claude-opus-4-7",
      "include_default_system_prompt": true,
      "system_prompt": "You are a Linear ticket groomer.",
      "visibility": null,
      "user_metadata": null,
      "auto_cleanup_on_idle": null
    },
    "vm_config": { "install_git_credentials": true, "install_user_secrets": true, "base_image_id": "<env-uuid>", "clone_repos": [], "resources": { "cpu_cores": 2, "memory_mib": 4096 } },
    ...
  }
}

The three highlighted session_config fields are null. The server's protobuf deserializer expects an i32 enum at session_config.visibility and rejects the request before any of the rest of the body is even considered.

Root cause

The apply path's J7n-equivalent (the YAML-bundle → API-config converter) builds sessionConfig with only three keys:

sessionConfig: {
  systemPrompt: r.systemPrompt,
  model: o,
  includeDefaultSystemPrompt: r.includeDefaultSystemPrompt,
}

…so visibility, userMetadata, and autoCleanupOnIdle are undefined on the JS object. That's correct, and JSON.stringify on its own would drop them.

But Cde (the config → snake_case API serializer) enumerates those keys explicitly:

...t.sessionConfig ? {
  session_config: {
    model: t.sessionConfig.model,
    include_default_system_prompt: t.sessionConfig.includeDefaultSystemPrompt,
    system_prompt: t.sessionConfig.systemPrompt,
    visibility: t.sessionConfig.visibility,           // undefined → null on the wire
    user_metadata: t.sessionConfig.userMetadata,      // undefined → null on the wire
    auto_cleanup_on_idle: t.sessionConfig.autoCleanupOnIdle, // undefined → null on the wire
  }
} : {}

Somewhere downstream of Cde (between the UDe.createExpert pass-through and the actual transport — looks like the protobuf-typed apiServer.createCloudExpert codec) those undefined values get materialized as JSON null rather than being omitted. Either the codec needs to skip undefined keys, or Cde needs to spread these conditionally the way it does for cpuCores, snapshotId, etc.:

...t.sessionConfig.visibility !== undefined ? { visibility: t.sessionConfig.visibility } : {},

What did you expect to happen?

auggie cloud expert apply -f bundle.yaml succeeds for any bundle that auggie cloud expert validate and apply --dry-run accept — including the round-trip case of apply-ing a fresh export with no edits. Optional unset fields should be omitted from the request body, not sent as null.

Steps to reproduce

  1. auggie cloud expert init --name diag-test -o /tmp/diag.yaml
  2. Fill in spec.expert.environment.id and spec.expert.model: claude-opus-4-7.
  3. auggie cloud expert validate -f /tmp/diag.yaml → passes.
  4. auggie cloud expert apply --dry-run -f /tmp/diag.yaml → passes.
  5. auggie cloud expert apply -f /tmp/diag.yaml400, expected i32.

Alternative reproduction (round-trip):

  1. auggie cloud expert export <any-existing-expert-id> -o /tmp/x.yaml
  2. auggie cloud expert apply -f /tmp/x.yaml → same 400.

Workaround

A NODE_OPTIONS=--require=<shim> that strips null-valued keys from POST /cloud-experts/experts/* bodies makes the apply succeed and the resulting expert is well-formed:

const origFetch = globalThis.fetch;
function clean(o) {
  if (Array.isArray(o)) return o.map(clean);
  if (o && typeof o === 'object') {
    const r = {};
    for (const [k, v] of Object.entries(o)) if (v !== null) r[k] = clean(v);
    return r;
  }
  return o;
}
globalThis.fetch = async (url, opts) => {
  if (opts?.method === 'POST' && opts.body && String(url).includes('/cloud-experts/experts/')) {
    try {
      opts = { ...opts, body: JSON.stringify(clean(JSON.parse(opts.body))) };
    } catch (_) {}
  }
  return origFetch(url, opts);
};

With the shim, apply succeeds and the expert is created correctly — confirming the server-side schema is fine and the CLI body is the only issue.

Auggie version

999.999.999 (commit b7668abcf77) — this is the build shipping inside Augment Cloud agent VMs (/opt/augment/augment.mjs, symlinked from /usr/local/bin/auggie). I have not verified against the published @augmentcode/auggie 0.24.0; the relevant code (Cde in the bundled augment.mjs) is internal CLI logic so the bug is likely present there too, but worth a quick check.

Request ID

1fc3ed43-a53b-4773-8841-b700e6b08b2e

(Other request IDs from the same session that hit the same error: 377b94f9-d9c6-4a10-bfe8-ff21556a8590, fc74fea3-fd1b-4850-b066-bb45252fea82, 960af909-4cc6-46d1-a0de-e96184402fec, e13887bb-2a81-4a79-a03c-2acc531639cd, 7689da7a-15bd-48a2-9486-c7c28fd59bdd, a46dec8a-bb7d-484f-b185-0f3a353f9145 — round-trip of Advisor export, b08024f3-72a5-4d59-bdf4-90f52d0512b7, 4da66c18-fc1c-4ef0-a39c-3579e22bf740, 11d5a401-57b6-48cf-9c45-3f3b9e44b102.)

Environment details

Environment
  • OS: Augment Cloud agent VM (Linux)
  • Shell: sh
  • Auggie binary: /opt/augment/augment.mjs (symlinked from /usr/local/bin/auggie)
  • Auggie version: 999.999.999 (commit b7668abcf77)
  • Tenant API: d15.api.augmentcode.com

Anything else we need to know?

The bug is fully blocking auggie cloud expert apply for any tenant on this CLI build — every fresh expert and every round-trip update fails. expert validate / apply --dry-run are happy because the bug is in the request body, not the bundle, so static checks miss it.

The minimal server-side-friendly fix is to spread the three optional session_config keys conditionally in Cde, the same way cpu_cores, memory_mib, snapshot_id, etc. are already handled in the same function. The same probably applies to any other undefined → null enum field — vm_config.image_source and routing_target.daemon_vm_id are already gated correctly; session_config.visibility/user_metadata/auto_cleanup_on_idle are not.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions