diff --git a/deno.jsonc b/deno.jsonc
index 0a135e8..e6cf584 100644
--- a/deno.jsonc
+++ b/deno.jsonc
@@ -20,6 +20,7 @@
     "./helper/echo": "./helper/echo.ts",
     "./helper/execute": "./helper/execute.ts",
     "./helper/expr_string": "./helper/expr_string.ts",
+    "./helper/getbufinfo": "./helper/getbufinfo.ts",
     "./helper/input": "./helper/input.ts",
     "./helper/keymap": "./helper/keymap.ts",
     "./helper/load": "./helper/load.ts",
diff --git a/helper/getbufinfo.ts b/helper/getbufinfo.ts
new file mode 100644
index 0000000..67cb768
--- /dev/null
+++ b/helper/getbufinfo.ts
@@ -0,0 +1,52 @@
+import type { Denops } from "../mod.ts";
+import type { BufInfo } from "../function/types.ts";
+import type { BufNameArg, GetBufInfoDictArg } from "../function/buffer.ts";
+
+/**
+ * Get buffer information.
+ *
+ * This is same as getbufinfo() function in Vim script, but filters the
+ * 'variables' entry in order to avoid errors when returning values from Vim
+ * script world into Deno world due to appearances of Funcrefs in buffer-local
+ * variables.
+ *
+ * ```typescript
+ * import type { Entrypoint } from "jsr:@denops/std";
+ * import { getbufinfo } from "jsr:@denops/std/helper/getbufinfo";
+ *
+ * export const main: Entrypoint = async (denops) => {
+ *   console.log(
+ *     await getbufinfo(denops, await denops.call("bufnr") as number),
+ *   );
+ * }
+ * ```
+ */
+export function getbufinfo(
+  denops: Denops,
+  buf?: BufNameArg,
+): Promise<BufInfo[]>;
+export function getbufinfo(
+  denops: Denops,
+  dict?: GetBufInfoDictArg,
+): Promise<BufInfo[]>;
+export async function getbufinfo(
+  denops: Denops,
+  ...args: unknown[]
+): Promise<BufInfo[]> {
+  const bufinfos = await denops.eval(
+    "map(call('getbufinfo', l:args), {_, v -> filter(v, {k -> k !=# 'variables'})})",
+    {
+      args: args,
+    },
+  ) as Record<
+    keyof BufInfo,
+    unknown
+  >[];
+  return bufinfos.map((bufinfo) => ({
+    ...bufinfo,
+    changed: !!bufinfo.changed,
+    hidden: !!bufinfo.hidden,
+    listed: !!bufinfo.listed,
+    loaded: !!bufinfo.loaded,
+  } as unknown as BufInfo));
+}
diff --git a/helper/getbufinfo_test.ts b/helper/getbufinfo_test.ts
new file mode 100644
index 0000000..afbc0eb
--- /dev/null
+++ b/helper/getbufinfo_test.ts
@@ -0,0 +1,77 @@
+import { getbufinfo } from "./getbufinfo.ts";
+import { assertEquals, assertFalse } from "@std/assert";
+import { assert, is } from "@core/unknownutil";
+import { test } from "@denops/test";
+import type { BufInfo } from "../function/types.ts";
+
+test({
+  mode: "all",
+  name: "buffer",
+  fn: async (denops, t) => {
+    await t.step({
+      name: "getbufinfo()",
+      fn: async () => {
+        await denops.cmd("enew!");
+        await denops.cmd("new foo");
+        await denops.call("setline", 1, "abcdef");
+        await denops.cmd("new bar");
+        await denops.cmd("hide");
+        await denops.cmd("new baz");
+        await denops.cmd("bunload!");
+
+        const actual = await getbufinfo(denops);
+        assert(actual, is.ArrayOf((x): x is BufInfo => is.Record(x)));
+        assertEquals(actual.length, 4);
+        assertEquals(
+          actual.map(({ changed, hidden, listed, loaded }) => (
+            { changed, hidden, listed, loaded }
+          )),
+          [
+            {
+              // [No Name]
+              changed: false,
+              hidden: false,
+              listed: true,
+              loaded: true,
+            },
+            {
+              // foo
+              changed: true,
+              hidden: false,
+              listed: true,
+              loaded: true,
+            },
+            {
+              // bar
+              changed: false,
+              hidden: true,
+              listed: true,
+              loaded: true,
+            },
+            {
+              // baz
+              changed: false,
+              hidden: false,
+              listed: true,
+              loaded: false,
+            },
+          ],
+          "boolean properties are invalid.",
+        );
+      },
+    });
+    await denops.cmd("1,$bwipeout!");
+
+    await t.step({
+      name: "getbufinfo() will filter buffer-local variables",
+      fn: async () => {
+        await denops.cmd("enew!");
+        await denops.cmd("let b:var = 0");
+
+        const actual = await getbufinfo(denops);
+        assertEquals(actual.length, 1);
+        assertFalse("variables" in actual[0]);
+      },
+    });
+  },
+});
diff --git a/helper/mod.ts b/helper/mod.ts
index 9f10239..c8efd1c 100644
--- a/helper/mod.ts
+++ b/helper/mod.ts
@@ -6,6 +6,7 @@
 export * from "./echo.ts";
 export * from "./execute.ts";
 export * from "./expr_string.ts";
+export * from "./getbufinfo.ts";
 export * from "./input.ts";
 export * from "./keymap.ts";
 export * from "./load.ts";