{"record":{"id":"e8c0e6307fd8fe98","repo":"unionlabs/union","slug":"no-return-value-from-channel-balance","errorCode":null,"errorMessage":"No return value from channel_balance","messagePattern":"No return value from channel_balance","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ts-sdk/src/sui/channel-balance.ts","lineNumber":45,"sourceCode":"      tx.pure.u32(config.channelId),\n      tx.pure.u256(path),\n      tx.pure(\"vector<u8>\", hexToBytes(token)),\n    ]\n\n    yield* Effect.log(\"Getting channel_balance for token:\", token)\n\n    const result = yield* readContract(\n      client,\n      \"0x835e6a7d0e415c0f1791ae61241f59e1dd9d669d59369cd056f02b3275f68779\",\n      contract_address,\n      module_id,\n      function_name,\n      [],\n      function_arguments,\n      tx,\n    )\n    if (!result || result.length === 0 || !result[0].returnValues || !result[0].returnValues[0]) {\n      throw new Error(\"No return value from channel_balance\")\n    }\n    const [bytesArray] = result[0].returnValues[0] as [number[], string]\n    const data = new Uint8Array(bytesArray)\n    const decoded = bcs.U256.parse(data)\n\n    return decoded\n  })\n","sourceCodeStart":27,"sourceCodeEnd":53,"githubUrl":"https://github.com/unionlabs/union/blob/031785bb6dc6b957c624e62bc64c184409c97d7b/ts-sdk/src/sui/channel-balance.ts#L27-L53","documentation":"channel_balance reads a Move function through a dev-inspect wrapper: readContract builds a moveCall on package 0x835e…779 and executes client.devInspectTransactionBlock, then the caller destructures result[0].returnValues[0] as BCS u256 bytes. If the simulation produces no return values — typically because the Move call aborted (e.g. no channel exists for the passed channel id) or the result shape is unexpected — this guard throws. Note the throw happens after readContract's own retry/timeout pipeline, inside Effect.gen, so it surfaces as a defect rather than SuiReadContractError.","triggerScenarios":"Passing a channel_id for which no channel object exists (channel not opened yet); wrong contract_address/module_id for the network; function_arguments mis-encoded so the Move entry point aborts during simulation.","commonSituations":"Querying the balance of a channel that has not been created yet; network mismatch between the SuiClient endpoint and the hardcoded package address; refactoring that changed argument order of the Move function.","solutions":["Verify the channel exists on the queried network and that channel_id/arguments are correct","Inspect the dev-inspect result before destructuring: aborted calls usually carry a failure status/error you can surface instead of the generic message","Catch the defect at the Effect boundary (Effect.catchDefect) and fall back to event data or fail with context"],"exampleFix":"// before\nif (!result || result.length === 0 || !result[0].returnValues || !result[0].returnValues[0]) {\n  throw new Error(\"No return value from channel_balance\")\n}\n\n// after\nconst r = result?.[0]\nconst status = (r as { status?: { status?: string; error?: string } })?.status\nif (status?.status === \"failure\") {\n  throw new Error(`channel_balance aborted: ${status.error ?? \"unknown Move abort\"}`)\n}\nif (!r?.returnValues?.[0]) {\n  throw new Error(\"No return value from channel_balance\")\n}","handlingStrategy":"try-catch","validationCode":"// confirm the channel object exists before asking for its balance\nconst exists = await client.getObject({ id: channelId })\nif (!exists.data) {\n  throw new Error(`Channel ${channelId} does not exist on this network`)\n}","typeGuard":"const hasReturnValue = (\n  r: { returnValues?: unknown[][] } | undefined,\n): r is { returnValues: [unknown[], ...unknown[][]] } =>\n  Array.isArray(r?.returnValues) && r.returnValues.length > 0 && Array.isArray(r.returnValues[0])","tryCatchPattern":"import { Effect, Exit } from \"effect\"\n\n// the throw happens inside Effect.gen → defect, not SuiReadContractError\nconst exit = await Effect.runPromiseExit(\n  program.pipe(Effect.catchDefect(e => Effect.fail(e))),\n)\nif (Exit.isFailure(exit)) {\n  // inspect dev-inspect status/args; verify channel_id and encoding\n}","preventionTips":["Verify channel ids and argument encoding against the Move source before calling","Check the dev-inspect status field for aborts instead of only returnValues","Wrap SDK reads with catchDefect so generic throws become handleable failures"],"tags":["sui","dev-inspect","move","channel"],"backgroundTag":null,"analyzedSha":"031785bb6dc6b957c624e62bc64c184409c97d7b","analyzedAt":"2026-08-16T06:24:09.996Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}